Roo/bootstrap/Table.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.celIndex).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         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
7397     },
7398
7399     autoOffset : function(){
7400         this.setDelta(0,0);
7401     }
7402 });/*
7403  * Based on:
7404  * Ext JS Library 1.1.1
7405  * Copyright(c) 2006-2007, Ext JS, LLC.
7406  *
7407  * Originally Released Under LGPL - original licence link has changed is not relivant.
7408  *
7409  * Fork - LGPL
7410  * <script type="text/javascript">
7411  */
7412
7413 /**
7414  * @class Roo.grid.AbstractSelectionModel
7415  * @extends Roo.util.Observable
7416  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7417  * implemented by descendant classes.  This class should not be directly instantiated.
7418  * @constructor
7419  */
7420 Roo.grid.AbstractSelectionModel = function(){
7421     this.locked = false;
7422     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7423 };
7424
7425 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7426     /** @ignore Called by the grid automatically. Do not call directly. */
7427     init : function(grid){
7428         this.grid = grid;
7429         this.initEvents();
7430     },
7431
7432     /**
7433      * Locks the selections.
7434      */
7435     lock : function(){
7436         this.locked = true;
7437     },
7438
7439     /**
7440      * Unlocks the selections.
7441      */
7442     unlock : function(){
7443         this.locked = false;
7444     },
7445
7446     /**
7447      * Returns true if the selections are locked.
7448      * @return {Boolean}
7449      */
7450     isLocked : function(){
7451         return this.locked;
7452     }
7453 });/*
7454  * Based on:
7455  * Ext JS Library 1.1.1
7456  * Copyright(c) 2006-2007, Ext JS, LLC.
7457  *
7458  * Originally Released Under LGPL - original licence link has changed is not relivant.
7459  *
7460  * Fork - LGPL
7461  * <script type="text/javascript">
7462  */
7463 /**
7464  * @extends Roo.grid.AbstractSelectionModel
7465  * @class Roo.grid.RowSelectionModel
7466  * The default SelectionModel used by {@link Roo.grid.Grid}.
7467  * It supports multiple selections and keyboard selection/navigation. 
7468  * @constructor
7469  * @param {Object} config
7470  */
7471 Roo.grid.RowSelectionModel = function(config){
7472     Roo.apply(this, config);
7473     this.selections = new Roo.util.MixedCollection(false, function(o){
7474         return o.id;
7475     });
7476
7477     this.last = false;
7478     this.lastActive = false;
7479
7480     this.addEvents({
7481         /**
7482         * @event selectionchange
7483         * Fires when the selection changes
7484         * @param {SelectionModel} this
7485         */
7486        "selectionchange" : true,
7487        /**
7488         * @event afterselectionchange
7489         * Fires after the selection changes (eg. by key press or clicking)
7490         * @param {SelectionModel} this
7491         */
7492        "afterselectionchange" : true,
7493        /**
7494         * @event beforerowselect
7495         * Fires when a row is selected being selected, return false to cancel.
7496         * @param {SelectionModel} this
7497         * @param {Number} rowIndex The selected index
7498         * @param {Boolean} keepExisting False if other selections will be cleared
7499         */
7500        "beforerowselect" : true,
7501        /**
7502         * @event rowselect
7503         * Fires when a row is selected.
7504         * @param {SelectionModel} this
7505         * @param {Number} rowIndex The selected index
7506         * @param {Roo.data.Record} r The record
7507         */
7508        "rowselect" : true,
7509        /**
7510         * @event rowdeselect
7511         * Fires when a row is deselected.
7512         * @param {SelectionModel} this
7513         * @param {Number} rowIndex The selected index
7514         */
7515         "rowdeselect" : true
7516     });
7517     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7518     this.locked = false;
7519 };
7520
7521 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7522     /**
7523      * @cfg {Boolean} singleSelect
7524      * True to allow selection of only one row at a time (defaults to false)
7525      */
7526     singleSelect : false,
7527
7528     // private
7529     initEvents : function(){
7530
7531         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7532             this.grid.on("mousedown", this.handleMouseDown, this);
7533         }else{ // allow click to work like normal
7534             this.grid.on("rowclick", this.handleDragableRowClick, this);
7535         }
7536         // bootstrap does not have a view..
7537         var view = this.grid.view ? this.grid.view : this.grid;
7538         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7539             "up" : function(e){
7540                 if(!e.shiftKey){
7541                     this.selectPrevious(e.shiftKey);
7542                 }else if(this.last !== false && this.lastActive !== false){
7543                     var last = this.last;
7544                     this.selectRange(this.last,  this.lastActive-1);
7545                     view.focusRow(this.lastActive);
7546                     if(last !== false){
7547                         this.last = last;
7548                     }
7549                 }else{
7550                     this.selectFirstRow();
7551                 }
7552                 this.fireEvent("afterselectionchange", this);
7553             },
7554             "down" : function(e){
7555                 if(!e.shiftKey){
7556                     this.selectNext(e.shiftKey);
7557                 }else if(this.last !== false && this.lastActive !== false){
7558                     var last = this.last;
7559                     this.selectRange(this.last,  this.lastActive+1);
7560                     view.focusRow(this.lastActive);
7561                     if(last !== false){
7562                         this.last = last;
7563                     }
7564                 }else{
7565                     this.selectFirstRow();
7566                 }
7567                 this.fireEvent("afterselectionchange", this);
7568             },
7569             scope: this
7570         });
7571
7572          
7573         view.on("refresh", this.onRefresh, this);
7574         view.on("rowupdated", this.onRowUpdated, this);
7575         view.on("rowremoved", this.onRemove, this);
7576     },
7577
7578     // private
7579     onRefresh : function(){
7580         var ds = this.grid.ds, i, v = this.grid.view;
7581         var s = this.selections;
7582         s.each(function(r){
7583             if((i = ds.indexOfId(r.id)) != -1){
7584                 v.onRowSelect(i);
7585                 s.add(ds.getAt(i)); // updating the selection relate data
7586             }else{
7587                 s.remove(r);
7588             }
7589         });
7590     },
7591
7592     // private
7593     onRemove : function(v, index, r){
7594         this.selections.remove(r);
7595     },
7596
7597     // private
7598     onRowUpdated : function(v, index, r){
7599         if(this.isSelected(r)){
7600             v.onRowSelect(index);
7601         }
7602     },
7603
7604     /**
7605      * Select records.
7606      * @param {Array} records The records to select
7607      * @param {Boolean} keepExisting (optional) True to keep existing selections
7608      */
7609     selectRecords : function(records, keepExisting){
7610         if(!keepExisting){
7611             this.clearSelections();
7612         }
7613         var ds = this.grid.ds;
7614         for(var i = 0, len = records.length; i < len; i++){
7615             this.selectRow(ds.indexOf(records[i]), true);
7616         }
7617     },
7618
7619     /**
7620      * Gets the number of selected rows.
7621      * @return {Number}
7622      */
7623     getCount : function(){
7624         return this.selections.length;
7625     },
7626
7627     /**
7628      * Selects the first row in the grid.
7629      */
7630     selectFirstRow : function(){
7631         this.selectRow(0);
7632     },
7633
7634     /**
7635      * Select the last row.
7636      * @param {Boolean} keepExisting (optional) True to keep existing selections
7637      */
7638     selectLastRow : function(keepExisting){
7639         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7640     },
7641
7642     /**
7643      * Selects the row immediately following the last selected row.
7644      * @param {Boolean} keepExisting (optional) True to keep existing selections
7645      */
7646     selectNext : function(keepExisting){
7647         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7648             this.selectRow(this.last+1, keepExisting);
7649             var view = this.grid.view ? this.grid.view : this.grid;
7650             view.focusRow(this.last);
7651         }
7652     },
7653
7654     /**
7655      * Selects the row that precedes the last selected row.
7656      * @param {Boolean} keepExisting (optional) True to keep existing selections
7657      */
7658     selectPrevious : function(keepExisting){
7659         if(this.last){
7660             this.selectRow(this.last-1, keepExisting);
7661             var view = this.grid.view ? this.grid.view : this.grid;
7662             view.focusRow(this.last);
7663         }
7664     },
7665
7666     /**
7667      * Returns the selected records
7668      * @return {Array} Array of selected records
7669      */
7670     getSelections : function(){
7671         return [].concat(this.selections.items);
7672     },
7673
7674     /**
7675      * Returns the first selected record.
7676      * @return {Record}
7677      */
7678     getSelected : function(){
7679         return this.selections.itemAt(0);
7680     },
7681
7682
7683     /**
7684      * Clears all selections.
7685      */
7686     clearSelections : function(fast){
7687         if(this.locked) {
7688             return;
7689         }
7690         if(fast !== true){
7691             var ds = this.grid.ds;
7692             var s = this.selections;
7693             s.each(function(r){
7694                 this.deselectRow(ds.indexOfId(r.id));
7695             }, this);
7696             s.clear();
7697         }else{
7698             this.selections.clear();
7699         }
7700         this.last = false;
7701     },
7702
7703
7704     /**
7705      * Selects all rows.
7706      */
7707     selectAll : function(){
7708         if(this.locked) {
7709             return;
7710         }
7711         this.selections.clear();
7712         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7713             this.selectRow(i, true);
7714         }
7715     },
7716
7717     /**
7718      * Returns True if there is a selection.
7719      * @return {Boolean}
7720      */
7721     hasSelection : function(){
7722         return this.selections.length > 0;
7723     },
7724
7725     /**
7726      * Returns True if the specified row is selected.
7727      * @param {Number/Record} record The record or index of the record to check
7728      * @return {Boolean}
7729      */
7730     isSelected : function(index){
7731         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7732         return (r && this.selections.key(r.id) ? true : false);
7733     },
7734
7735     /**
7736      * Returns True if the specified record id is selected.
7737      * @param {String} id The id of record to check
7738      * @return {Boolean}
7739      */
7740     isIdSelected : function(id){
7741         return (this.selections.key(id) ? true : false);
7742     },
7743
7744     // private
7745     handleMouseDown : function(e, t)
7746     {
7747         var view = this.grid.view ? this.grid.view : this.grid;
7748         var rowIndex;
7749         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7750             return;
7751         };
7752         if(e.shiftKey && this.last !== false){
7753             var last = this.last;
7754             this.selectRange(last, rowIndex, e.ctrlKey);
7755             this.last = last; // reset the last
7756             view.focusRow(rowIndex);
7757         }else{
7758             var isSelected = this.isSelected(rowIndex);
7759             if(e.button !== 0 && isSelected){
7760                 view.focusRow(rowIndex);
7761             }else if(e.ctrlKey && isSelected){
7762                 this.deselectRow(rowIndex);
7763             }else if(!isSelected){
7764                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7765                 view.focusRow(rowIndex);
7766             }
7767         }
7768         this.fireEvent("afterselectionchange", this);
7769     },
7770     // private
7771     handleDragableRowClick :  function(grid, rowIndex, e) 
7772     {
7773         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7774             this.selectRow(rowIndex, false);
7775             var view = this.grid.view ? this.grid.view : this.grid;
7776             view.focusRow(rowIndex);
7777              this.fireEvent("afterselectionchange", this);
7778         }
7779     },
7780     
7781     /**
7782      * Selects multiple rows.
7783      * @param {Array} rows Array of the indexes of the row to select
7784      * @param {Boolean} keepExisting (optional) True to keep existing selections
7785      */
7786     selectRows : function(rows, keepExisting){
7787         if(!keepExisting){
7788             this.clearSelections();
7789         }
7790         for(var i = 0, len = rows.length; i < len; i++){
7791             this.selectRow(rows[i], true);
7792         }
7793     },
7794
7795     /**
7796      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7797      * @param {Number} startRow The index of the first row in the range
7798      * @param {Number} endRow The index of the last row in the range
7799      * @param {Boolean} keepExisting (optional) True to retain existing selections
7800      */
7801     selectRange : function(startRow, endRow, keepExisting){
7802         if(this.locked) {
7803             return;
7804         }
7805         if(!keepExisting){
7806             this.clearSelections();
7807         }
7808         if(startRow <= endRow){
7809             for(var i = startRow; i <= endRow; i++){
7810                 this.selectRow(i, true);
7811             }
7812         }else{
7813             for(var i = startRow; i >= endRow; i--){
7814                 this.selectRow(i, true);
7815             }
7816         }
7817     },
7818
7819     /**
7820      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7821      * @param {Number} startRow The index of the first row in the range
7822      * @param {Number} endRow The index of the last row in the range
7823      */
7824     deselectRange : function(startRow, endRow, preventViewNotify){
7825         if(this.locked) {
7826             return;
7827         }
7828         for(var i = startRow; i <= endRow; i++){
7829             this.deselectRow(i, preventViewNotify);
7830         }
7831     },
7832
7833     /**
7834      * Selects a row.
7835      * @param {Number} row The index of the row to select
7836      * @param {Boolean} keepExisting (optional) True to keep existing selections
7837      */
7838     selectRow : function(index, keepExisting, preventViewNotify){
7839         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7840             return;
7841         }
7842         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7843             if(!keepExisting || this.singleSelect){
7844                 this.clearSelections();
7845             }
7846             var r = this.grid.ds.getAt(index);
7847             this.selections.add(r);
7848             this.last = this.lastActive = index;
7849             if(!preventViewNotify){
7850                 var view = this.grid.view ? this.grid.view : this.grid;
7851                 view.onRowSelect(index);
7852             }
7853             this.fireEvent("rowselect", this, index, r);
7854             this.fireEvent("selectionchange", this);
7855         }
7856     },
7857
7858     /**
7859      * Deselects a row.
7860      * @param {Number} row The index of the row to deselect
7861      */
7862     deselectRow : function(index, preventViewNotify){
7863         if(this.locked) {
7864             return;
7865         }
7866         if(this.last == index){
7867             this.last = false;
7868         }
7869         if(this.lastActive == index){
7870             this.lastActive = false;
7871         }
7872         var r = this.grid.ds.getAt(index);
7873         this.selections.remove(r);
7874         if(!preventViewNotify){
7875             var view = this.grid.view ? this.grid.view : this.grid;
7876             view.onRowDeselect(index);
7877         }
7878         this.fireEvent("rowdeselect", this, index);
7879         this.fireEvent("selectionchange", this);
7880     },
7881
7882     // private
7883     restoreLast : function(){
7884         if(this._last){
7885             this.last = this._last;
7886         }
7887     },
7888
7889     // private
7890     acceptsNav : function(row, col, cm){
7891         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7892     },
7893
7894     // private
7895     onEditorKey : function(field, e){
7896         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7897         if(k == e.TAB){
7898             e.stopEvent();
7899             ed.completeEdit();
7900             if(e.shiftKey){
7901                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7902             }else{
7903                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7904             }
7905         }else if(k == e.ENTER && !e.ctrlKey){
7906             e.stopEvent();
7907             ed.completeEdit();
7908             if(e.shiftKey){
7909                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7910             }else{
7911                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7912             }
7913         }else if(k == e.ESC){
7914             ed.cancelEdit();
7915         }
7916         if(newCell){
7917             g.startEditing(newCell[0], newCell[1]);
7918         }
7919     }
7920 });/*
7921  * Based on:
7922  * Ext JS Library 1.1.1
7923  * Copyright(c) 2006-2007, Ext JS, LLC.
7924  *
7925  * Originally Released Under LGPL - original licence link has changed is not relivant.
7926  *
7927  * Fork - LGPL
7928  * <script type="text/javascript">
7929  */
7930  
7931
7932 /**
7933  * @class Roo.grid.ColumnModel
7934  * @extends Roo.util.Observable
7935  * This is the default implementation of a ColumnModel used by the Grid. It defines
7936  * the columns in the grid.
7937  * <br>Usage:<br>
7938  <pre><code>
7939  var colModel = new Roo.grid.ColumnModel([
7940         {header: "Ticker", width: 60, sortable: true, locked: true},
7941         {header: "Company Name", width: 150, sortable: true},
7942         {header: "Market Cap.", width: 100, sortable: true},
7943         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7944         {header: "Employees", width: 100, sortable: true, resizable: false}
7945  ]);
7946  </code></pre>
7947  * <p>
7948  
7949  * The config options listed for this class are options which may appear in each
7950  * individual column definition.
7951  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7952  * @constructor
7953  * @param {Object} config An Array of column config objects. See this class's
7954  * config objects for details.
7955 */
7956 Roo.grid.ColumnModel = function(config){
7957         /**
7958      * The config passed into the constructor
7959      */
7960     this.config = []; //config;
7961     this.lookup = {};
7962
7963     // if no id, create one
7964     // if the column does not have a dataIndex mapping,
7965     // map it to the order it is in the config
7966     for(var i = 0, len = config.length; i < len; i++){
7967         this.addColumn(config[i]);
7968         
7969     }
7970
7971     /**
7972      * The width of columns which have no width specified (defaults to 100)
7973      * @type Number
7974      */
7975     this.defaultWidth = 100;
7976
7977     /**
7978      * Default sortable of columns which have no sortable specified (defaults to false)
7979      * @type Boolean
7980      */
7981     this.defaultSortable = false;
7982
7983     this.addEvents({
7984         /**
7985              * @event widthchange
7986              * Fires when the width of a column changes.
7987              * @param {ColumnModel} this
7988              * @param {Number} columnIndex The column index
7989              * @param {Number} newWidth The new width
7990              */
7991             "widthchange": true,
7992         /**
7993              * @event headerchange
7994              * Fires when the text of a header changes.
7995              * @param {ColumnModel} this
7996              * @param {Number} columnIndex The column index
7997              * @param {Number} newText The new header text
7998              */
7999             "headerchange": true,
8000         /**
8001              * @event hiddenchange
8002              * Fires when a column is hidden or "unhidden".
8003              * @param {ColumnModel} this
8004              * @param {Number} columnIndex The column index
8005              * @param {Boolean} hidden true if hidden, false otherwise
8006              */
8007             "hiddenchange": true,
8008             /**
8009          * @event columnmoved
8010          * Fires when a column is moved.
8011          * @param {ColumnModel} this
8012          * @param {Number} oldIndex
8013          * @param {Number} newIndex
8014          */
8015         "columnmoved" : true,
8016         /**
8017          * @event columlockchange
8018          * Fires when a column's locked state is changed
8019          * @param {ColumnModel} this
8020          * @param {Number} colIndex
8021          * @param {Boolean} locked true if locked
8022          */
8023         "columnlockchange" : true
8024     });
8025     Roo.grid.ColumnModel.superclass.constructor.call(this);
8026 };
8027 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8028     /**
8029      * @cfg {String} header The header text to display in the Grid view.
8030      */
8031     /**
8032      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8033      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8034      * specified, the column's index is used as an index into the Record's data Array.
8035      */
8036     /**
8037      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8038      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8039      */
8040     /**
8041      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8042      * Defaults to the value of the {@link #defaultSortable} property.
8043      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8044      */
8045     /**
8046      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8047      */
8048     /**
8049      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8050      */
8051     /**
8052      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8053      */
8054     /**
8055      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8056      */
8057     /**
8058      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8059      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8060      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8061      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8062      */
8063        /**
8064      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8065      */
8066     /**
8067      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8068      */
8069     /**
8070      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8071      */
8072     /**
8073      * @cfg {String} cursor (Optional)
8074      */
8075     /**
8076      * @cfg {String} tooltip (Optional)
8077      */
8078     /**
8079      * @cfg {Number} xs (Optional)
8080      */
8081     /**
8082      * @cfg {Number} sm (Optional)
8083      */
8084     /**
8085      * @cfg {Number} md (Optional)
8086      */
8087     /**
8088      * @cfg {Number} lg (Optional)
8089      */
8090     /**
8091      * Returns the id of the column at the specified index.
8092      * @param {Number} index The column index
8093      * @return {String} the id
8094      */
8095     getColumnId : function(index){
8096         return this.config[index].id;
8097     },
8098
8099     /**
8100      * Returns the column for a specified id.
8101      * @param {String} id The column id
8102      * @return {Object} the column
8103      */
8104     getColumnById : function(id){
8105         return this.lookup[id];
8106     },
8107
8108     
8109     /**
8110      * Returns the column Object for a specified dataIndex.
8111      * @param {String} dataIndex The column dataIndex
8112      * @return {Object|Boolean} the column or false if not found
8113      */
8114     getColumnByDataIndex: function(dataIndex){
8115         var index = this.findColumnIndex(dataIndex);
8116         return index > -1 ? this.config[index] : false;
8117     },
8118     
8119     /**
8120      * Returns the index for a specified column id.
8121      * @param {String} id The column id
8122      * @return {Number} the index, or -1 if not found
8123      */
8124     getIndexById : function(id){
8125         for(var i = 0, len = this.config.length; i < len; i++){
8126             if(this.config[i].id == id){
8127                 return i;
8128             }
8129         }
8130         return -1;
8131     },
8132     
8133     /**
8134      * Returns the index for a specified column dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Number} the index, or -1 if not found
8137      */
8138     
8139     findColumnIndex : function(dataIndex){
8140         for(var i = 0, len = this.config.length; i < len; i++){
8141             if(this.config[i].dataIndex == dataIndex){
8142                 return i;
8143             }
8144         }
8145         return -1;
8146     },
8147     
8148     
8149     moveColumn : function(oldIndex, newIndex){
8150         var c = this.config[oldIndex];
8151         this.config.splice(oldIndex, 1);
8152         this.config.splice(newIndex, 0, c);
8153         this.dataMap = null;
8154         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8155     },
8156
8157     isLocked : function(colIndex){
8158         return this.config[colIndex].locked === true;
8159     },
8160
8161     setLocked : function(colIndex, value, suppressEvent){
8162         if(this.isLocked(colIndex) == value){
8163             return;
8164         }
8165         this.config[colIndex].locked = value;
8166         if(!suppressEvent){
8167             this.fireEvent("columnlockchange", this, colIndex, value);
8168         }
8169     },
8170
8171     getTotalLockedWidth : function(){
8172         var totalWidth = 0;
8173         for(var i = 0; i < this.config.length; i++){
8174             if(this.isLocked(i) && !this.isHidden(i)){
8175                 this.totalWidth += this.getColumnWidth(i);
8176             }
8177         }
8178         return totalWidth;
8179     },
8180
8181     getLockedCount : function(){
8182         for(var i = 0, len = this.config.length; i < len; i++){
8183             if(!this.isLocked(i)){
8184                 return i;
8185             }
8186         }
8187         
8188         return this.config.length;
8189     },
8190
8191     /**
8192      * Returns the number of columns.
8193      * @return {Number}
8194      */
8195     getColumnCount : function(visibleOnly){
8196         if(visibleOnly === true){
8197             var c = 0;
8198             for(var i = 0, len = this.config.length; i < len; i++){
8199                 if(!this.isHidden(i)){
8200                     c++;
8201                 }
8202             }
8203             return c;
8204         }
8205         return this.config.length;
8206     },
8207
8208     /**
8209      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8210      * @param {Function} fn
8211      * @param {Object} scope (optional)
8212      * @return {Array} result
8213      */
8214     getColumnsBy : function(fn, scope){
8215         var r = [];
8216         for(var i = 0, len = this.config.length; i < len; i++){
8217             var c = this.config[i];
8218             if(fn.call(scope||this, c, i) === true){
8219                 r[r.length] = c;
8220             }
8221         }
8222         return r;
8223     },
8224
8225     /**
8226      * Returns true if the specified column is sortable.
8227      * @param {Number} col The column index
8228      * @return {Boolean}
8229      */
8230     isSortable : function(col){
8231         if(typeof this.config[col].sortable == "undefined"){
8232             return this.defaultSortable;
8233         }
8234         return this.config[col].sortable;
8235     },
8236
8237     /**
8238      * Returns the rendering (formatting) function defined for the column.
8239      * @param {Number} col The column index.
8240      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8241      */
8242     getRenderer : function(col){
8243         if(!this.config[col].renderer){
8244             return Roo.grid.ColumnModel.defaultRenderer;
8245         }
8246         return this.config[col].renderer;
8247     },
8248
8249     /**
8250      * Sets the rendering (formatting) function for a column.
8251      * @param {Number} col The column index
8252      * @param {Function} fn The function to use to process the cell's raw data
8253      * to return HTML markup for the grid view. The render function is called with
8254      * the following parameters:<ul>
8255      * <li>Data value.</li>
8256      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8257      * <li>css A CSS style string to apply to the table cell.</li>
8258      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8259      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8260      * <li>Row index</li>
8261      * <li>Column index</li>
8262      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8263      */
8264     setRenderer : function(col, fn){
8265         this.config[col].renderer = fn;
8266     },
8267
8268     /**
8269      * Returns the width for the specified column.
8270      * @param {Number} col The column index
8271      * @return {Number}
8272      */
8273     getColumnWidth : function(col){
8274         return this.config[col].width * 1 || this.defaultWidth;
8275     },
8276
8277     /**
8278      * Sets the width for a column.
8279      * @param {Number} col The column index
8280      * @param {Number} width The new width
8281      */
8282     setColumnWidth : function(col, width, suppressEvent){
8283         this.config[col].width = width;
8284         this.totalWidth = null;
8285         if(!suppressEvent){
8286              this.fireEvent("widthchange", this, col, width);
8287         }
8288     },
8289
8290     /**
8291      * Returns the total width of all columns.
8292      * @param {Boolean} includeHidden True to include hidden column widths
8293      * @return {Number}
8294      */
8295     getTotalWidth : function(includeHidden){
8296         if(!this.totalWidth){
8297             this.totalWidth = 0;
8298             for(var i = 0, len = this.config.length; i < len; i++){
8299                 if(includeHidden || !this.isHidden(i)){
8300                     this.totalWidth += this.getColumnWidth(i);
8301                 }
8302             }
8303         }
8304         return this.totalWidth;
8305     },
8306
8307     /**
8308      * Returns the header for the specified column.
8309      * @param {Number} col The column index
8310      * @return {String}
8311      */
8312     getColumnHeader : function(col){
8313         return this.config[col].header;
8314     },
8315
8316     /**
8317      * Sets the header for a column.
8318      * @param {Number} col The column index
8319      * @param {String} header The new header
8320      */
8321     setColumnHeader : function(col, header){
8322         this.config[col].header = header;
8323         this.fireEvent("headerchange", this, col, header);
8324     },
8325
8326     /**
8327      * Returns the tooltip for the specified column.
8328      * @param {Number} col The column index
8329      * @return {String}
8330      */
8331     getColumnTooltip : function(col){
8332             return this.config[col].tooltip;
8333     },
8334     /**
8335      * Sets the tooltip for a column.
8336      * @param {Number} col The column index
8337      * @param {String} tooltip The new tooltip
8338      */
8339     setColumnTooltip : function(col, tooltip){
8340             this.config[col].tooltip = tooltip;
8341     },
8342
8343     /**
8344      * Returns the dataIndex for the specified column.
8345      * @param {Number} col The column index
8346      * @return {Number}
8347      */
8348     getDataIndex : function(col){
8349         return this.config[col].dataIndex;
8350     },
8351
8352     /**
8353      * Sets the dataIndex for a column.
8354      * @param {Number} col The column index
8355      * @param {Number} dataIndex The new dataIndex
8356      */
8357     setDataIndex : function(col, dataIndex){
8358         this.config[col].dataIndex = dataIndex;
8359     },
8360
8361     
8362     
8363     /**
8364      * Returns true if the cell is editable.
8365      * @param {Number} colIndex The column index
8366      * @param {Number} rowIndex The row index - this is nto actually used..?
8367      * @return {Boolean}
8368      */
8369     isCellEditable : function(colIndex, rowIndex){
8370         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8371     },
8372
8373     /**
8374      * Returns the editor defined for the cell/column.
8375      * return false or null to disable editing.
8376      * @param {Number} colIndex The column index
8377      * @param {Number} rowIndex The row index
8378      * @return {Object}
8379      */
8380     getCellEditor : function(colIndex, rowIndex){
8381         return this.config[colIndex].editor;
8382     },
8383
8384     /**
8385      * Sets if a column is editable.
8386      * @param {Number} col The column index
8387      * @param {Boolean} editable True if the column is editable
8388      */
8389     setEditable : function(col, editable){
8390         this.config[col].editable = editable;
8391     },
8392
8393
8394     /**
8395      * Returns true if the column is hidden.
8396      * @param {Number} colIndex The column index
8397      * @return {Boolean}
8398      */
8399     isHidden : function(colIndex){
8400         return this.config[colIndex].hidden;
8401     },
8402
8403
8404     /**
8405      * Returns true if the column width cannot be changed
8406      */
8407     isFixed : function(colIndex){
8408         return this.config[colIndex].fixed;
8409     },
8410
8411     /**
8412      * Returns true if the column can be resized
8413      * @return {Boolean}
8414      */
8415     isResizable : function(colIndex){
8416         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8417     },
8418     /**
8419      * Sets if a column is hidden.
8420      * @param {Number} colIndex The column index
8421      * @param {Boolean} hidden True if the column is hidden
8422      */
8423     setHidden : function(colIndex, hidden){
8424         this.config[colIndex].hidden = hidden;
8425         this.totalWidth = null;
8426         this.fireEvent("hiddenchange", this, colIndex, hidden);
8427     },
8428
8429     /**
8430      * Sets the editor for a column.
8431      * @param {Number} col The column index
8432      * @param {Object} editor The editor object
8433      */
8434     setEditor : function(col, editor){
8435         this.config[col].editor = editor;
8436     },
8437     /**
8438      * Add a column (experimental...) - defaults to adding to the end..
8439      * @param {Object} config 
8440     */
8441     addColumn : function(c)
8442     {
8443     
8444         var i = this.config.length;
8445         this.config[i] = c;
8446         
8447         if(typeof c.dataIndex == "undefined"){
8448             c.dataIndex = i;
8449         }
8450         if(typeof c.renderer == "string"){
8451             c.renderer = Roo.util.Format[c.renderer];
8452         }
8453         if(typeof c.id == "undefined"){
8454             c.id = Roo.id();
8455         }
8456         if(c.editor && c.editor.xtype){
8457             c.editor  = Roo.factory(c.editor, Roo.grid);
8458         }
8459         if(c.editor && c.editor.isFormField){
8460             c.editor = new Roo.grid.GridEditor(c.editor);
8461         }
8462         this.lookup[c.id] = c;
8463     }
8464     
8465 });
8466
8467 Roo.grid.ColumnModel.defaultRenderer = function(value)
8468 {
8469     if(typeof value == "object") {
8470         return value;
8471     }
8472         if(typeof value == "string" && value.length < 1){
8473             return "&#160;";
8474         }
8475     
8476         return String.format("{0}", value);
8477 };
8478
8479 // Alias for backwards compatibility
8480 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8481 /*
8482  * Based on:
8483  * Ext JS Library 1.1.1
8484  * Copyright(c) 2006-2007, Ext JS, LLC.
8485  *
8486  * Originally Released Under LGPL - original licence link has changed is not relivant.
8487  *
8488  * Fork - LGPL
8489  * <script type="text/javascript">
8490  */
8491  
8492 /**
8493  * @class Roo.LoadMask
8494  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8495  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8496  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8497  * element's UpdateManager load indicator and will be destroyed after the initial load.
8498  * @constructor
8499  * Create a new LoadMask
8500  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8501  * @param {Object} config The config object
8502  */
8503 Roo.LoadMask = function(el, config){
8504     this.el = Roo.get(el);
8505     Roo.apply(this, config);
8506     if(this.store){
8507         this.store.on('beforeload', this.onBeforeLoad, this);
8508         this.store.on('load', this.onLoad, this);
8509         this.store.on('loadexception', this.onLoadException, this);
8510         this.removeMask = false;
8511     }else{
8512         var um = this.el.getUpdateManager();
8513         um.showLoadIndicator = false; // disable the default indicator
8514         um.on('beforeupdate', this.onBeforeLoad, this);
8515         um.on('update', this.onLoad, this);
8516         um.on('failure', this.onLoad, this);
8517         this.removeMask = true;
8518     }
8519 };
8520
8521 Roo.LoadMask.prototype = {
8522     /**
8523      * @cfg {Boolean} removeMask
8524      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8525      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8526      */
8527     /**
8528      * @cfg {String} msg
8529      * The text to display in a centered loading message box (defaults to 'Loading...')
8530      */
8531     msg : 'Loading...',
8532     /**
8533      * @cfg {String} msgCls
8534      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8535      */
8536     msgCls : 'x-mask-loading',
8537
8538     /**
8539      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8540      * @type Boolean
8541      */
8542     disabled: false,
8543
8544     /**
8545      * Disables the mask to prevent it from being displayed
8546      */
8547     disable : function(){
8548        this.disabled = true;
8549     },
8550
8551     /**
8552      * Enables the mask so that it can be displayed
8553      */
8554     enable : function(){
8555         this.disabled = false;
8556     },
8557     
8558     onLoadException : function()
8559     {
8560         Roo.log(arguments);
8561         
8562         if (typeof(arguments[3]) != 'undefined') {
8563             Roo.MessageBox.alert("Error loading",arguments[3]);
8564         } 
8565         /*
8566         try {
8567             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8568                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8569             }   
8570         } catch(e) {
8571             
8572         }
8573         */
8574     
8575         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8576     },
8577     // private
8578     onLoad : function()
8579     {
8580         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8581     },
8582
8583     // private
8584     onBeforeLoad : function(){
8585         if(!this.disabled){
8586             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8587         }
8588     },
8589
8590     // private
8591     destroy : function(){
8592         if(this.store){
8593             this.store.un('beforeload', this.onBeforeLoad, this);
8594             this.store.un('load', this.onLoad, this);
8595             this.store.un('loadexception', this.onLoadException, this);
8596         }else{
8597             var um = this.el.getUpdateManager();
8598             um.un('beforeupdate', this.onBeforeLoad, this);
8599             um.un('update', this.onLoad, this);
8600             um.un('failure', this.onLoad, this);
8601         }
8602     }
8603 };/**
8604  * @class Roo.bootstrap.Table
8605  * @licence LGBL
8606  * @extends Roo.bootstrap.Component
8607  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8608  * Similar to Roo.grid.Grid
8609  * <pre><code>
8610  var table = Roo.factory({
8611     xtype : 'Table',
8612     xns : Roo.bootstrap,
8613     autoSizeColumns: true,
8614     
8615     
8616     store : {
8617         xtype : 'Store',
8618         xns : Roo.data,
8619         remoteSort : true,
8620         sortInfo : { direction : 'ASC', field: 'name' },
8621         proxy : {
8622            xtype : 'HttpProxy',
8623            xns : Roo.data,
8624            method : 'GET',
8625            url : 'https://example.com/some.data.url.json'
8626         },
8627         reader : {
8628            xtype : 'JsonReader',
8629            xns : Roo.data,
8630            fields : [ 'id', 'name', whatever' ],
8631            id : 'id',
8632            root : 'data'
8633         }
8634     },
8635     cm : [
8636         {
8637             xtype : 'ColumnModel',
8638             xns : Roo.grid,
8639             align : 'center',
8640             cursor : 'pointer',
8641             dataIndex : 'is_in_group',
8642             header : "Name",
8643             sortable : true,
8644             renderer : function(v, x , r) {  
8645             
8646                 return String.format("{0}", v)
8647             }
8648             width : 3
8649         } // more columns..
8650     ],
8651     selModel : {
8652         xtype : 'RowSelectionModel',
8653         xns : Roo.bootstrap.Table
8654         // you can add listeners to catch selection change here....
8655     }
8656      
8657
8658  });
8659  // set any options
8660  grid.render(Roo.get("some-div"));
8661 </code></pre>
8662
8663 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8664
8665
8666
8667  *
8668  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8669  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8670  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8671  * 
8672  * @cfg {String} cls table class
8673  *
8674  * 
8675  * @cfg {boolean} striped Should the rows be alternative striped
8676  * @cfg {boolean} bordered Add borders to the table
8677  * @cfg {boolean} hover Add hover highlighting
8678  * @cfg {boolean} condensed Format condensed
8679  * @cfg {boolean} responsive Format condensed
8680  * @cfg {Boolean} loadMask (true|false) default false
8681  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8682  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8683  * @cfg {Boolean} rowSelection (true|false) default false
8684  * @cfg {Boolean} cellSelection (true|false) default false
8685  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8686  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8687  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8688  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8689  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8690  * @cfg {Number} minColumnWidth default 10 pixels minimum column width 
8691  * 
8692  * @constructor
8693  * Create a new Table
8694  * @param {Object} config The config object
8695  */
8696
8697 Roo.bootstrap.Table = function(config)
8698 {
8699     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8700      
8701     // BC...
8702     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8703     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8704     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8705     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8706     
8707     this.view = this; // compat with grid.
8708     
8709     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8710     if (this.sm) {
8711         this.sm.grid = this;
8712         this.selModel = Roo.factory(this.sm, Roo.grid);
8713         this.sm = this.selModel;
8714         this.sm.xmodule = this.xmodule || false;
8715     }
8716     
8717     if (this.cm && typeof(this.cm.config) == 'undefined') {
8718         this.colModel = new Roo.grid.ColumnModel(this.cm);
8719         this.cm = this.colModel;
8720         this.cm.xmodule = this.xmodule || false;
8721     }
8722     if (this.store) {
8723         this.store= Roo.factory(this.store, Roo.data);
8724         this.ds = this.store;
8725         this.ds.xmodule = this.xmodule || false;
8726          
8727     }
8728     if (this.footer && this.store) {
8729         this.footer.dataSource = this.ds;
8730         this.footer = Roo.factory(this.footer);
8731     }
8732     
8733     /** @private */
8734     this.addEvents({
8735         /**
8736          * @event cellclick
8737          * Fires when a cell is clicked
8738          * @param {Roo.bootstrap.Table} this
8739          * @param {Roo.Element} el
8740          * @param {Number} rowIndex
8741          * @param {Number} columnIndex
8742          * @param {Roo.EventObject} e
8743          */
8744         "cellclick" : true,
8745         /**
8746          * @event celldblclick
8747          * Fires when a cell is double clicked
8748          * @param {Roo.bootstrap.Table} this
8749          * @param {Roo.Element} el
8750          * @param {Number} rowIndex
8751          * @param {Number} columnIndex
8752          * @param {Roo.EventObject} e
8753          */
8754         "celldblclick" : true,
8755         /**
8756          * @event rowclick
8757          * Fires when a row is clicked
8758          * @param {Roo.bootstrap.Table} this
8759          * @param {Roo.Element} el
8760          * @param {Number} rowIndex
8761          * @param {Roo.EventObject} e
8762          */
8763         "rowclick" : true,
8764         /**
8765          * @event rowdblclick
8766          * Fires when a row is double clicked
8767          * @param {Roo.bootstrap.Table} this
8768          * @param {Roo.Element} el
8769          * @param {Number} rowIndex
8770          * @param {Roo.EventObject} e
8771          */
8772         "rowdblclick" : true,
8773         /**
8774          * @event mouseover
8775          * Fires when a mouseover occur
8776          * @param {Roo.bootstrap.Table} this
8777          * @param {Roo.Element} el
8778          * @param {Number} rowIndex
8779          * @param {Number} columnIndex
8780          * @param {Roo.EventObject} e
8781          */
8782         "mouseover" : true,
8783         /**
8784          * @event mouseout
8785          * Fires when a mouseout occur
8786          * @param {Roo.bootstrap.Table} this
8787          * @param {Roo.Element} el
8788          * @param {Number} rowIndex
8789          * @param {Number} columnIndex
8790          * @param {Roo.EventObject} e
8791          */
8792         "mouseout" : true,
8793         /**
8794          * @event rowclass
8795          * Fires when a row is rendered, so you can change add a style to it.
8796          * @param {Roo.bootstrap.Table} this
8797          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8798          */
8799         'rowclass' : true,
8800           /**
8801          * @event rowsrendered
8802          * Fires when all the  rows have been rendered
8803          * @param {Roo.bootstrap.Table} this
8804          */
8805         'rowsrendered' : true,
8806         /**
8807          * @event contextmenu
8808          * The raw contextmenu event for the entire grid.
8809          * @param {Roo.EventObject} e
8810          */
8811         "contextmenu" : true,
8812         /**
8813          * @event rowcontextmenu
8814          * Fires when a row is right clicked
8815          * @param {Roo.bootstrap.Table} this
8816          * @param {Number} rowIndex
8817          * @param {Roo.EventObject} e
8818          */
8819         "rowcontextmenu" : true,
8820         /**
8821          * @event cellcontextmenu
8822          * Fires when a cell is right clicked
8823          * @param {Roo.bootstrap.Table} this
8824          * @param {Number} rowIndex
8825          * @param {Number} cellIndex
8826          * @param {Roo.EventObject} e
8827          */
8828          "cellcontextmenu" : true,
8829          /**
8830          * @event headercontextmenu
8831          * Fires when a header is right clicked
8832          * @param {Roo.bootstrap.Table} this
8833          * @param {Number} columnIndex
8834          * @param {Roo.EventObject} e
8835          */
8836         "headercontextmenu" : true,
8837         /**
8838          * @event mousedown
8839          * The raw mousedown event for the entire grid.
8840          * @param {Roo.EventObject} e
8841          */
8842         "mousedown" : true
8843         
8844     });
8845 };
8846
8847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8848     
8849     cls: false,
8850     
8851     striped : false,
8852     scrollBody : false,
8853     bordered: false,
8854     hover:  false,
8855     condensed : false,
8856     responsive : false,
8857     sm : false,
8858     cm : false,
8859     store : false,
8860     loadMask : false,
8861     footerShow : true,
8862     headerShow : true,
8863     enableColumnResize: true,
8864   
8865     rowSelection : false,
8866     cellSelection : false,
8867     layout : false,
8868
8869     minColumnWidth : 10,
8870     
8871     // Roo.Element - the tbody
8872     bodyEl: false,  // <tbody> Roo.Element - thead element    
8873     headEl: false,  // <thead> Roo.Element - thead element
8874     resizeProxy : false, // proxy element for dragging?
8875
8876
8877     
8878     container: false, // used by gridpanel...
8879     
8880     lazyLoad : false,
8881     
8882     CSS : Roo.util.CSS,
8883     
8884     auto_hide_footer : false,
8885     
8886     view: false, // actually points to this..
8887     
8888     getAutoCreate : function()
8889     {
8890         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8891         
8892         cfg = {
8893             tag: 'table',
8894             cls : 'table', 
8895             cn : []
8896         };
8897         // this get's auto added by panel.Grid
8898         if (this.scrollBody) {
8899             cfg.cls += ' table-body-fixed';
8900         }    
8901         if (this.striped) {
8902             cfg.cls += ' table-striped';
8903         }
8904         
8905         if (this.hover) {
8906             cfg.cls += ' table-hover';
8907         }
8908         if (this.bordered) {
8909             cfg.cls += ' table-bordered';
8910         }
8911         if (this.condensed) {
8912             cfg.cls += ' table-condensed';
8913         }
8914         
8915         if (this.responsive) {
8916             cfg.cls += ' table-responsive';
8917         }
8918         
8919         if (this.cls) {
8920             cfg.cls+=  ' ' +this.cls;
8921         }
8922         
8923         
8924         
8925         if (this.layout) {
8926             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8927         }
8928         
8929         if(this.store || this.cm){
8930             if(this.headerShow){
8931                 cfg.cn.push(this.renderHeader());
8932             }
8933             
8934             cfg.cn.push(this.renderBody());
8935             
8936             if(this.footerShow){
8937                 cfg.cn.push(this.renderFooter());
8938             }
8939             // where does this come from?
8940             //cfg.cls+=  ' TableGrid';
8941         }
8942         
8943         return { cn : [ cfg ] };
8944     },
8945     
8946     initEvents : function()
8947     {   
8948         if(!this.store || !this.cm){
8949             return;
8950         }
8951         if (this.selModel) {
8952             this.selModel.initEvents();
8953         }
8954         
8955         
8956         //Roo.log('initEvents with ds!!!!');
8957         
8958         this.bodyEl = this.el.select('tbody', true).first();
8959         this.headEl = this.el.select('thead', true).first();
8960         this.mainFoot = this.el.select('tfoot', true).first();
8961         
8962         
8963         
8964         
8965         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8966             e.on('click', this.sort, this);
8967         }, this);
8968         
8969         
8970         // why is this done????? = it breaks dialogs??
8971         //this.parent().el.setStyle('position', 'relative');
8972         
8973         
8974         if (this.footer) {
8975             this.footer.parentId = this.id;
8976             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8977             
8978             if(this.lazyLoad){
8979                 this.el.select('tfoot tr td').first().addClass('hide');
8980             }
8981         } 
8982         
8983         if(this.loadMask) {
8984             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8985         }
8986         
8987         this.store.on('load', this.onLoad, this);
8988         this.store.on('beforeload', this.onBeforeLoad, this);
8989         this.store.on('update', this.onUpdate, this);
8990         this.store.on('add', this.onAdd, this);
8991         this.store.on("clear", this.clear, this);
8992         
8993         this.el.on("contextmenu", this.onContextMenu, this);
8994         
8995         
8996         this.cm.on("headerchange", this.onHeaderChange, this);
8997         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8998
8999  //?? does bodyEl get replaced on render?
9000         this.bodyEl.on("click", this.onClick, this);
9001         this.bodyEl.on("dblclick", this.onDblClick, this);        
9002         this.bodyEl.on('scroll', this.onBodyScroll, this);
9003
9004         // guessing mainbody will work - this relays usually caught by selmodel at present.
9005         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9006   
9007   
9008         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9009         
9010   
9011         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9012             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9013         }
9014         
9015         
9016     },
9017     // Compatibility with grid - we implement all the view features at present.
9018     getView : function()
9019     {
9020         return this;
9021     },
9022     
9023     onContextMenu : function(e, t)
9024     {
9025         this.processEvent("contextmenu", e);
9026     },
9027     
9028     processEvent : function(name, e)
9029     {
9030         if (name != 'touchstart' ) {
9031             this.fireEvent(name, e);    
9032         }
9033         
9034         var t = e.getTarget();
9035         
9036         var cell = Roo.get(t);
9037         
9038         if(!cell){
9039             return;
9040         }
9041         
9042         if(cell.findParent('tfoot', false, true)){
9043             return;
9044         }
9045         
9046         if(cell.findParent('thead', false, true)){
9047             
9048             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9049                 cell = Roo.get(t).findParent('th', false, true);
9050                 if (!cell) {
9051                     Roo.log("failed to find th in thead?");
9052                     Roo.log(e.getTarget());
9053                     return;
9054                 }
9055             }
9056             
9057             var cellIndex = cell.dom.cellIndex;
9058             
9059             var ename = name == 'touchstart' ? 'click' : name;
9060             this.fireEvent("header" + ename, this, cellIndex, e);
9061             
9062             return;
9063         }
9064         
9065         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9066             cell = Roo.get(t).findParent('td', false, true);
9067             if (!cell) {
9068                 Roo.log("failed to find th in tbody?");
9069                 Roo.log(e.getTarget());
9070                 return;
9071             }
9072         }
9073         
9074         var row = cell.findParent('tr', false, true);
9075         var cellIndex = cell.dom.cellIndex;
9076         var rowIndex = row.dom.rowIndex - 1;
9077         
9078         if(row !== false){
9079             
9080             this.fireEvent("row" + name, this, rowIndex, e);
9081             
9082             if(cell !== false){
9083             
9084                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9085             }
9086         }
9087         
9088     },
9089     
9090     onMouseover : function(e, el)
9091     {
9092         var cell = Roo.get(el);
9093         
9094         if(!cell){
9095             return;
9096         }
9097         
9098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9099             cell = cell.findParent('td', false, true);
9100         }
9101         
9102         var row = cell.findParent('tr', false, true);
9103         var cellIndex = cell.dom.cellIndex;
9104         var rowIndex = row.dom.rowIndex - 1; // start from 0
9105         
9106         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9107         
9108     },
9109     
9110     onMouseout : function(e, el)
9111     {
9112         var cell = Roo.get(el);
9113         
9114         if(!cell){
9115             return;
9116         }
9117         
9118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9119             cell = cell.findParent('td', false, true);
9120         }
9121         
9122         var row = cell.findParent('tr', false, true);
9123         var cellIndex = cell.dom.cellIndex;
9124         var rowIndex = row.dom.rowIndex - 1; // start from 0
9125         
9126         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9127         
9128     },
9129     
9130     onClick : function(e, el)
9131     {
9132         var cell = Roo.get(el);
9133         
9134         if(!cell || (!this.cellSelection && !this.rowSelection)){
9135             return;
9136         }
9137         
9138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9139             cell = cell.findParent('td', false, true);
9140         }
9141         
9142         if(!cell || typeof(cell) == 'undefined'){
9143             return;
9144         }
9145         
9146         var row = cell.findParent('tr', false, true);
9147         
9148         if(!row || typeof(row) == 'undefined'){
9149             return;
9150         }
9151         
9152         var cellIndex = cell.dom.cellIndex;
9153         var rowIndex = this.getRowIndex(row);
9154         
9155         // why??? - should these not be based on SelectionModel?
9156         //if(this.cellSelection){
9157             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9158         //}
9159         
9160         //if(this.rowSelection){
9161             this.fireEvent('rowclick', this, row, rowIndex, e);
9162         //}
9163          
9164     },
9165         
9166     onDblClick : function(e,el)
9167     {
9168         var cell = Roo.get(el);
9169         
9170         if(!cell || (!this.cellSelection && !this.rowSelection)){
9171             return;
9172         }
9173         
9174         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9175             cell = cell.findParent('td', false, true);
9176         }
9177         
9178         if(!cell || typeof(cell) == 'undefined'){
9179             return;
9180         }
9181         
9182         var row = cell.findParent('tr', false, true);
9183         
9184         if(!row || typeof(row) == 'undefined'){
9185             return;
9186         }
9187         
9188         var cellIndex = cell.dom.cellIndex;
9189         var rowIndex = this.getRowIndex(row);
9190         
9191         if(this.cellSelection){
9192             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9193         }
9194         
9195         if(this.rowSelection){
9196             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9197         }
9198     },
9199     findRowIndex : function(el)
9200     {
9201         var cell = Roo.get(el);
9202         if(!cell) {
9203             return false;
9204         }
9205         var row = cell.findParent('tr', false, true);
9206         
9207         if(!row || typeof(row) == 'undefined'){
9208             return false;
9209         }
9210         return this.getRowIndex(row);
9211     },
9212     sort : function(e,el)
9213     {
9214         var col = Roo.get(el);
9215         
9216         if(!col.hasClass('sortable')){
9217             return;
9218         }
9219         
9220         var sort = col.attr('sort');
9221         var dir = 'ASC';
9222         
9223         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9224             dir = 'DESC';
9225         }
9226         
9227         this.store.sortInfo = {field : sort, direction : dir};
9228         
9229         if (this.footer) {
9230             Roo.log("calling footer first");
9231             this.footer.onClick('first');
9232         } else {
9233         
9234             this.store.load({ params : { start : 0 } });
9235         }
9236     },
9237     
9238     renderHeader : function()
9239     {
9240         var header = {
9241             tag: 'thead',
9242             cn : []
9243         };
9244         
9245         var cm = this.cm;
9246         this.totalWidth = 0;
9247         
9248         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9249             
9250             var config = cm.config[i];
9251             
9252             var c = {
9253                 tag: 'th',
9254                 cls : 'x-hcol-' + i,
9255                 style : '',
9256                 
9257                 html: cm.getColumnHeader(i)
9258             };
9259             
9260             var tooltip = cm.getColumnTooltip(i);
9261             if (tooltip) {
9262                 c.tooltip = tooltip;
9263             }
9264             
9265             
9266             var hh = '';
9267             
9268             if(typeof(config.sortable) != 'undefined' && config.sortable){
9269                 c.cls += ' sortable';
9270                 c.html = '<i class="fa"></i>' + c.html;
9271             }
9272             
9273             // could use BS4 hidden-..-down 
9274             
9275             if(typeof(config.lgHeader) != 'undefined'){
9276                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9277             }
9278             
9279             if(typeof(config.mdHeader) != 'undefined'){
9280                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9281             }
9282             
9283             if(typeof(config.smHeader) != 'undefined'){
9284                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9285             }
9286             
9287             if(typeof(config.xsHeader) != 'undefined'){
9288                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9289             }
9290             
9291             if(hh.length){
9292                 c.html = hh;
9293             }
9294             
9295             if(typeof(config.tooltip) != 'undefined'){
9296                 c.tooltip = config.tooltip;
9297             }
9298             
9299             if(typeof(config.colspan) != 'undefined'){
9300                 c.colspan = config.colspan;
9301             }
9302             
9303             if(typeof(config.hidden) != 'undefined' && config.hidden){
9304                 c.cls += ' d-none';
9305             } else {
9306                 c.cls += ' d-block';
9307             }
9308             
9309             if(typeof(config.dataIndex) != 'undefined'){
9310                 c.sort = config.dataIndex;
9311             }
9312             
9313            
9314             
9315             if(typeof(config.align) != 'undefined' && config.align.length){
9316                 c.style += ' text-align:' + config.align + ';';
9317             }
9318             
9319             if(typeof(config.width) != 'undefined'){
9320                 c.style += ' width:' + config.width + 'px;';
9321                 this.totalWidth += config.width;
9322             } else {
9323                 this.totalWidth += 100; // assume minimum of 100 per column?
9324             }
9325             
9326             if(typeof(config.cls) != 'undefined'){
9327                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9328             }
9329             // this is the bit that doesnt reall work at all...
9330             
9331             
9332             
9333             ['xs','sm','md','lg'].map(function(size){
9334                 
9335                 if(typeof(config[size]) == 'undefined'){
9336                     return;
9337                 }
9338                  
9339                 if (!config[size]) { // 0 = hidden
9340                     // BS 4 '0' is treated as hide that column and below.
9341                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9342                     return;
9343                 }
9344                 
9345                 c.cls += ' col-' + size + '-' + config[size] + (
9346                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9347                 );
9348                 
9349                 
9350             });
9351             // at the end?
9352             if (i > 0) {
9353                 c.html +=' <span class="x-grid-split x-grid-split-' + (i-1) + '"></span>';
9354             }
9355             
9356             
9357             
9358             header.cn.push(c)
9359         }
9360         
9361         return header;
9362     },
9363     
9364     renderBody : function()
9365     {
9366         var body = {
9367             tag: 'tbody',
9368             cn : [
9369                 {
9370                     tag: 'tr',
9371                     cn : [
9372                         {
9373                             tag : 'td',
9374                             colspan :  this.cm.getColumnCount()
9375                         }
9376                     ]
9377                 }
9378             ]
9379         };
9380         
9381         return body;
9382     },
9383     
9384     renderFooter : function()
9385     {
9386         var footer = {
9387             tag: 'tfoot',
9388             cn : [
9389                 {
9390                     tag: 'tr',
9391                     cn : [
9392                         {
9393                             tag : 'td',
9394                             colspan :  this.cm.getColumnCount()
9395                         }
9396                     ]
9397                 }
9398             ]
9399         };
9400         
9401         return footer;
9402     },
9403     
9404     
9405     
9406     onLoad : function()
9407     {
9408 //        Roo.log('ds onload');
9409         this.clear();
9410         
9411         var _this = this;
9412         var cm = this.cm;
9413         var ds = this.store;
9414         
9415         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9416             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9417             if (_this.store.sortInfo) {
9418                     
9419                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9420                     e.select('i', true).addClass(['fa-arrow-up']);
9421                 }
9422                 
9423                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9424                     e.select('i', true).addClass(['fa-arrow-down']);
9425                 }
9426             }
9427         });
9428         
9429         var tbody =  this.bodyEl;
9430               
9431         if(ds.getCount() > 0){
9432             ds.data.each(function(d,rowIndex){
9433                 var row =  this.renderRow(cm, ds, rowIndex);
9434                 
9435                 tbody.createChild(row);
9436                 
9437                 var _this = this;
9438                 
9439                 if(row.cellObjects.length){
9440                     Roo.each(row.cellObjects, function(r){
9441                         _this.renderCellObject(r);
9442                     })
9443                 }
9444                 
9445             }, this);
9446         }
9447         
9448         var tfoot = this.el.select('tfoot', true).first();
9449         
9450         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9451             
9452             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9453             
9454             var total = this.ds.getTotalCount();
9455             
9456             if(this.footer.pageSize < total){
9457                 this.mainFoot.show();
9458             }
9459         }
9460         
9461         Roo.each(this.el.select('tbody td', true).elements, function(e){
9462             e.on('mouseover', _this.onMouseover, _this);
9463         });
9464         
9465         Roo.each(this.el.select('tbody td', true).elements, function(e){
9466             e.on('mouseout', _this.onMouseout, _this);
9467         });
9468         this.fireEvent('rowsrendered', this);
9469         
9470         this.autoSize();
9471     },
9472     
9473     
9474     onUpdate : function(ds,record)
9475     {
9476         this.refreshRow(record);
9477         this.autoSize();
9478     },
9479     
9480     onRemove : function(ds, record, index, isUpdate){
9481         if(isUpdate !== true){
9482             this.fireEvent("beforerowremoved", this, index, record);
9483         }
9484         var bt = this.bodyEl.dom;
9485         
9486         var rows = this.el.select('tbody > tr', true).elements;
9487         
9488         if(typeof(rows[index]) != 'undefined'){
9489             bt.removeChild(rows[index].dom);
9490         }
9491         
9492 //        if(bt.rows[index]){
9493 //            bt.removeChild(bt.rows[index]);
9494 //        }
9495         
9496         if(isUpdate !== true){
9497             //this.stripeRows(index);
9498             //this.syncRowHeights(index, index);
9499             //this.layout();
9500             this.fireEvent("rowremoved", this, index, record);
9501         }
9502     },
9503     
9504     onAdd : function(ds, records, rowIndex)
9505     {
9506         //Roo.log('on Add called');
9507         // - note this does not handle multiple adding very well..
9508         var bt = this.bodyEl.dom;
9509         for (var i =0 ; i < records.length;i++) {
9510             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9511             //Roo.log(records[i]);
9512             //Roo.log(this.store.getAt(rowIndex+i));
9513             this.insertRow(this.store, rowIndex + i, false);
9514             return;
9515         }
9516         
9517     },
9518     
9519     
9520     refreshRow : function(record){
9521         var ds = this.store, index;
9522         if(typeof record == 'number'){
9523             index = record;
9524             record = ds.getAt(index);
9525         }else{
9526             index = ds.indexOf(record);
9527             if (index < 0) {
9528                 return; // should not happen - but seems to 
9529             }
9530         }
9531         this.insertRow(ds, index, true);
9532         this.autoSize();
9533         this.onRemove(ds, record, index+1, true);
9534         this.autoSize();
9535         //this.syncRowHeights(index, index);
9536         //this.layout();
9537         this.fireEvent("rowupdated", this, index, record);
9538     },
9539     
9540     onRowSelect : function(rowIndex){
9541         var row = this.getRowDom(rowIndex);
9542         row.addClass(['bg-info','info']);
9543     },
9544
9545     onRowDeselect : function(rowIndex){
9546         var row = this.getRowDom(rowIndex);
9547         row.removeClass(['bg-info','info']);
9548     },
9549       /**
9550      * Focuses the specified row.
9551      * @param {Number} row The row index
9552      */
9553     focusRow : function(row)
9554     {
9555         //Roo.log('GridView.focusRow');
9556         var x = this.bodyEl.dom.scrollLeft;
9557         this.focusCell(row, 0, false);
9558         this.bodyEl.dom.scrollLeft = x;
9559
9560     },
9561      /**
9562      * Focuses the specified cell.
9563      * @param {Number} row The row index
9564      * @param {Number} col The column index
9565      * @param {Boolean} hscroll false to disable horizontal scrolling
9566      */
9567     focusCell : function(row, col, hscroll)
9568     {
9569         //Roo.log('GridView.focusCell');
9570         var el = this.ensureVisible(row, col, hscroll);
9571         // not sure what focusEL achives = it's a <a> pos relative 
9572         //this.focusEl.alignTo(el, "tl-tl");
9573         //if(Roo.isGecko){
9574         //    this.focusEl.focus();
9575         //}else{
9576         //    this.focusEl.focus.defer(1, this.focusEl);
9577         //}
9578     },
9579     
9580      /**
9581      * Scrolls the specified cell into view
9582      * @param {Number} row The row index
9583      * @param {Number} col The column index
9584      * @param {Boolean} hscroll false to disable horizontal scrolling
9585      */
9586     ensureVisible : function(row, col, hscroll)
9587     {
9588         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9589         //return null; //disable for testing.
9590         if(typeof row != "number"){
9591             row = row.rowIndex;
9592         }
9593         if(row < 0 && row >= this.ds.getCount()){
9594             return  null;
9595         }
9596         col = (col !== undefined ? col : 0);
9597         var cm = this.cm;
9598         while(cm.isHidden(col)){
9599             col++;
9600         }
9601
9602         var el = this.getCellDom(row, col);
9603         if(!el){
9604             return null;
9605         }
9606         var c = this.bodyEl.dom;
9607
9608         var ctop = parseInt(el.offsetTop, 10);
9609         var cleft = parseInt(el.offsetLeft, 10);
9610         var cbot = ctop + el.offsetHeight;
9611         var cright = cleft + el.offsetWidth;
9612
9613         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9614         var ch = 0; //?? header is not withing the area?
9615         var stop = parseInt(c.scrollTop, 10);
9616         var sleft = parseInt(c.scrollLeft, 10);
9617         var sbot = stop + ch;
9618         var sright = sleft + c.clientWidth;
9619         /*
9620         Roo.log('GridView.ensureVisible:' +
9621                 ' ctop:' + ctop +
9622                 ' c.clientHeight:' + c.clientHeight +
9623                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9624                 ' stop:' + stop +
9625                 ' cbot:' + cbot +
9626                 ' sbot:' + sbot +
9627                 ' ch:' + ch  
9628                 );
9629         */
9630         if(ctop < stop){
9631             c.scrollTop = ctop;
9632             //Roo.log("set scrolltop to ctop DISABLE?");
9633         }else if(cbot > sbot){
9634             //Roo.log("set scrolltop to cbot-ch");
9635             c.scrollTop = cbot-ch;
9636         }
9637
9638         if(hscroll !== false){
9639             if(cleft < sleft){
9640                 c.scrollLeft = cleft;
9641             }else if(cright > sright){
9642                 c.scrollLeft = cright-c.clientWidth;
9643             }
9644         }
9645
9646         return el;
9647     },
9648     
9649     
9650     insertRow : function(dm, rowIndex, isUpdate){
9651         
9652         if(!isUpdate){
9653             this.fireEvent("beforerowsinserted", this, rowIndex);
9654         }
9655             //var s = this.getScrollState();
9656         var row = this.renderRow(this.cm, this.store, rowIndex);
9657         // insert before rowIndex..
9658         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9659         
9660         var _this = this;
9661                 
9662         if(row.cellObjects.length){
9663             Roo.each(row.cellObjects, function(r){
9664                 _this.renderCellObject(r);
9665             })
9666         }
9667             
9668         if(!isUpdate){
9669             this.fireEvent("rowsinserted", this, rowIndex);
9670             //this.syncRowHeights(firstRow, lastRow);
9671             //this.stripeRows(firstRow);
9672             //this.layout();
9673         }
9674         
9675     },
9676     
9677     
9678     getRowDom : function(rowIndex)
9679     {
9680         var rows = this.el.select('tbody > tr', true).elements;
9681         
9682         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9683         
9684     },
9685     getCellDom : function(rowIndex, colIndex)
9686     {
9687         var row = this.getRowDom(rowIndex);
9688         if (row === false) {
9689             return false;
9690         }
9691         var cols = row.select('td', true).elements;
9692         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9693         
9694     },
9695     
9696     // returns the object tree for a tr..
9697   
9698     
9699     renderRow : function(cm, ds, rowIndex) 
9700     {
9701         var d = ds.getAt(rowIndex);
9702         
9703         var row = {
9704             tag : 'tr',
9705             cls : 'x-row-' + rowIndex,
9706             cn : []
9707         };
9708             
9709         var cellObjects = [];
9710         
9711         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9712             var config = cm.config[i];
9713             
9714             var renderer = cm.getRenderer(i);
9715             var value = '';
9716             var id = false;
9717             
9718             if(typeof(renderer) !== 'undefined'){
9719                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9720             }
9721             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9722             // and are rendered into the cells after the row is rendered - using the id for the element.
9723             
9724             if(typeof(value) === 'object'){
9725                 id = Roo.id();
9726                 cellObjects.push({
9727                     container : id,
9728                     cfg : value 
9729                 })
9730             }
9731             
9732             var rowcfg = {
9733                 record: d,
9734                 rowIndex : rowIndex,
9735                 colIndex : i,
9736                 rowClass : ''
9737             };
9738
9739             this.fireEvent('rowclass', this, rowcfg);
9740             
9741             var td = {
9742                 tag: 'td',
9743                 // this might end up displaying HTML?
9744                 // this is too messy... - better to only do it on columsn you know are going to be too long
9745                 //tooltip : (typeof(value) === 'object') ? '' : value,
9746                 cls : rowcfg.rowClass + ' x-col-' + i,
9747                 style: '',
9748                 html: (typeof(value) === 'object') ? '' : value
9749             };
9750             
9751             if (id) {
9752                 td.id = id;
9753             }
9754             
9755             if(typeof(config.colspan) != 'undefined'){
9756                 td.colspan = config.colspan;
9757             }
9758             
9759             if(typeof(config.hidden) != 'undefined' && config.hidden){
9760                 td.cls += ' d-none';
9761             } else {
9762                 td.cls += ' d-block';
9763             }
9764             
9765             if(typeof(config.align) != 'undefined' && config.align.length){
9766                 td.style += ' text-align:' + config.align + ';';
9767             }
9768             if(typeof(config.valign) != 'undefined' && config.valign.length){
9769                 td.style += ' vertical-align:' + config.valign + ';';
9770             }
9771             
9772             if(typeof(config.width) != 'undefined'){
9773                 td.style += ' width:' +  config.width + 'px;';
9774             }
9775             
9776             if(typeof(config.cursor) != 'undefined'){
9777                 td.style += ' cursor:' +  config.cursor + ';';
9778             }
9779             
9780             if(typeof(config.cls) != 'undefined'){
9781                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9782             }
9783             
9784             ['xs','sm','md','lg'].map(function(size){
9785                 
9786                 if(typeof(config[size]) == 'undefined'){
9787                     return;
9788                 }
9789                 
9790                 
9791                   
9792                 if (!config[size]) { // 0 = hidden
9793                     // BS 4 '0' is treated as hide that column and below.
9794                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9795                     return;
9796                 }
9797                 
9798                 td.cls += ' col-' + size + '-' + config[size] + (
9799                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9800                 );
9801                  
9802
9803             });
9804             
9805             row.cn.push(td);
9806            
9807         }
9808         
9809         row.cellObjects = cellObjects;
9810         
9811         return row;
9812           
9813     },
9814     
9815     
9816     
9817     onBeforeLoad : function()
9818     {
9819         
9820     },
9821      /**
9822      * Remove all rows
9823      */
9824     clear : function()
9825     {
9826         this.el.select('tbody', true).first().dom.innerHTML = '';
9827     },
9828     /**
9829      * Show or hide a row.
9830      * @param {Number} rowIndex to show or hide
9831      * @param {Boolean} state hide
9832      */
9833     setRowVisibility : function(rowIndex, state)
9834     {
9835         var bt = this.bodyEl.dom;
9836         
9837         var rows = this.el.select('tbody > tr', true).elements;
9838         
9839         if(typeof(rows[rowIndex]) == 'undefined'){
9840             return;
9841         }
9842         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9843         
9844     },
9845     
9846     
9847     getSelectionModel : function(){
9848         if(!this.selModel){
9849             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9850         }
9851         return this.selModel;
9852     },
9853     /*
9854      * Render the Roo.bootstrap object from renderder
9855      */
9856     renderCellObject : function(r)
9857     {
9858         var _this = this;
9859         
9860         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9861         
9862         var t = r.cfg.render(r.container);
9863         
9864         if(r.cfg.cn){
9865             Roo.each(r.cfg.cn, function(c){
9866                 var child = {
9867                     container: t.getChildContainer(),
9868                     cfg: c
9869                 };
9870                 _this.renderCellObject(child);
9871             })
9872         }
9873     },
9874     /**
9875      * get the Row Index from a dom element.
9876      * @param {Roo.Element} row The row to look for
9877      * @returns {Number} the row
9878      */
9879     getRowIndex : function(row)
9880     {
9881         var rowIndex = -1;
9882         
9883         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9884             if(el != row){
9885                 return;
9886             }
9887             
9888             rowIndex = index;
9889         });
9890         
9891         return rowIndex;
9892     },
9893     /**
9894      * get the header TH element for columnIndex
9895      * @param {Number} columnIndex
9896      * @returns {Roo.Element}
9897      */
9898     getHeaderIndex: function(colIndex)
9899     {
9900         var cols = this.headEl.select('th', true).elements;
9901         return cols[colIndex]; 
9902     },
9903     /**
9904      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
9905      * @param {domElement} cell to look for
9906      * @returns {Number} the column
9907      */
9908     getCellIndex : function(cell)
9909     {
9910         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
9911         if(id){
9912             return parseInt(id[1], 10);
9913         }
9914         return 0;
9915     },
9916      /**
9917      * Returns the grid's underlying element = used by panel.Grid
9918      * @return {Element} The element
9919      */
9920     getGridEl : function(){
9921         return this.el;
9922     },
9923      /**
9924      * Forces a resize - used by panel.Grid
9925      * @return {Element} The element
9926      */
9927     autoSize : function()
9928     {
9929         //var ctr = Roo.get(this.container.dom.parentElement);
9930         var ctr = Roo.get(this.el.dom);
9931         
9932         var thd = this.getGridEl().select('thead',true).first();
9933         var tbd = this.getGridEl().select('tbody', true).first();
9934         var tfd = this.getGridEl().select('tfoot', true).first();
9935         
9936         var cw = ctr.getWidth();
9937         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9938         
9939         if (tbd) {
9940             
9941             tbd.setWidth(ctr.getWidth());
9942             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9943             // this needs fixing for various usage - currently only hydra job advers I think..
9944             //tdb.setHeight(
9945             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9946             //); 
9947             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9948             cw -= barsize;
9949         }
9950         cw = Math.max(cw, this.totalWidth);
9951         this.getGridEl().select('tbody tr',true).setWidth(cw);
9952         
9953         // resize 'expandable coloumn?
9954         
9955         return; // we doe not have a view in this design..
9956         
9957     },
9958     onBodyScroll: function()
9959     {
9960         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9961         if(this.headEl){
9962             this.headEl.setStyle({
9963                 'position' : 'relative',
9964                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
9965             });
9966         }
9967         
9968         if(this.lazyLoad){
9969             
9970             var scrollHeight = this.bodyEl.dom.scrollHeight;
9971             
9972             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
9973             
9974             var height = this.bodyEl.getHeight();
9975             
9976             if(scrollHeight - height == scrollTop) {
9977                 
9978                 var total = this.ds.getTotalCount();
9979                 
9980                 if(this.footer.cursor + this.footer.pageSize < total){
9981                     
9982                     this.footer.ds.load({
9983                         params : {
9984                             start : this.footer.cursor + this.footer.pageSize,
9985                             limit : this.footer.pageSize
9986                         },
9987                         add : true
9988                     });
9989                 }
9990             }
9991             
9992         }
9993     },
9994     onColumnSplitterMoved : function()
9995     {
9996         // resize it..  
9997         
9998         
9999     },
10000     onHeaderChange : function()
10001     {
10002         var header = this.renderHeader();
10003         var table = this.el.select('table', true).first();
10004         
10005         this.headEl.remove();
10006         this.headEl = table.createChild(header, this.bodyEl, false);
10007         
10008         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10009             e.on('click', this.sort, this);
10010         }, this);
10011         
10012         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10013             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10014         }
10015         
10016     },
10017     
10018     onHiddenChange : function(colModel, colIndex, hidden)
10019     {
10020         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10021         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10022         
10023         //this.CSS.updateRule(thSelector, "display", "");
10024         var cols = this.headEl.select('th', true).elements;
10025         if (typeof(cols[colIndex]) != 'undefined') {
10026             cols[colIndex].removeClass(['d-none', 'd-block']);
10027             cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
10028         }
10029         this.CSS.updateRule(tdSelector, "display", "");
10030         
10031         if(hidden){
10032           //  this.CSS.updateRule(thSelector, "display", "none");
10033             this.CSS.updateRule(tdSelector, "display", "none");
10034         }
10035         
10036         this.onHeaderChange();
10037         this.onLoad();
10038     },
10039     
10040     setColumnWidth: function(col_index, width)
10041     {
10042         // width = "md-2 xs-2..."
10043         if(!this.colModel.config[col_index]) {
10044             return;
10045         }
10046         
10047         var w = width.split(" ");
10048         
10049         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10050         
10051         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10052         
10053         
10054         for(var j = 0; j < w.length; j++) {
10055             
10056             if(!w[j]) {
10057                 continue;
10058             }
10059             
10060             var size_cls = w[j].split("-");
10061             
10062             if(!Number.isInteger(size_cls[1] * 1)) {
10063                 continue;
10064             }
10065             
10066             if(!this.colModel.config[col_index][size_cls[0]]) {
10067                 continue;
10068             }
10069             
10070             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10071                 continue;
10072             }
10073             
10074             h_row[0].classList.replace(
10075                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10076                 "col-"+size_cls[0]+"-"+size_cls[1]
10077             );
10078             
10079             for(var i = 0; i < rows.length; i++) {
10080                 
10081                 var size_cls = w[j].split("-");
10082                 
10083                 if(!Number.isInteger(size_cls[1] * 1)) {
10084                     continue;
10085                 }
10086                 
10087                 if(!this.colModel.config[col_index][size_cls[0]]) {
10088                     continue;
10089                 }
10090                 
10091                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10092                     continue;
10093                 }
10094                 
10095                 rows[i].classList.replace(
10096                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10097                     "col-"+size_cls[0]+"-"+size_cls[1]
10098                 );
10099             }
10100             
10101             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10102         }
10103     }
10104 });
10105
10106 // currently only used to find the split on drag.. 
10107 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10108
10109 /**
10110  * @depricated
10111 */
10112 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10113 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
10114  * - LGPL
10115  *
10116  * table cell
10117  * 
10118  */
10119
10120 /**
10121  * @class Roo.bootstrap.TableCell
10122  * @extends Roo.bootstrap.Component
10123  * Bootstrap TableCell class
10124  * @cfg {String} html cell contain text
10125  * @cfg {String} cls cell class
10126  * @cfg {String} tag cell tag (td|th) default td
10127  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10128  * @cfg {String} align Aligns the content in a cell
10129  * @cfg {String} axis Categorizes cells
10130  * @cfg {String} bgcolor Specifies the background color of a cell
10131  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10132  * @cfg {Number} colspan Specifies the number of columns a cell should span
10133  * @cfg {String} headers Specifies one or more header cells a cell is related to
10134  * @cfg {Number} height Sets the height of a cell
10135  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10136  * @cfg {Number} rowspan Sets the number of rows a cell should span
10137  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10138  * @cfg {String} valign Vertical aligns the content in a cell
10139  * @cfg {Number} width Specifies the width of a cell
10140  * 
10141  * @constructor
10142  * Create a new TableCell
10143  * @param {Object} config The config object
10144  */
10145
10146 Roo.bootstrap.TableCell = function(config){
10147     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10148 };
10149
10150 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10151     
10152     html: false,
10153     cls: false,
10154     tag: false,
10155     abbr: false,
10156     align: false,
10157     axis: false,
10158     bgcolor: false,
10159     charoff: false,
10160     colspan: false,
10161     headers: false,
10162     height: false,
10163     nowrap: false,
10164     rowspan: false,
10165     scope: false,
10166     valign: false,
10167     width: false,
10168     
10169     
10170     getAutoCreate : function(){
10171         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10172         
10173         cfg = {
10174             tag: 'td'
10175         };
10176         
10177         if(this.tag){
10178             cfg.tag = this.tag;
10179         }
10180         
10181         if (this.html) {
10182             cfg.html=this.html
10183         }
10184         if (this.cls) {
10185             cfg.cls=this.cls
10186         }
10187         if (this.abbr) {
10188             cfg.abbr=this.abbr
10189         }
10190         if (this.align) {
10191             cfg.align=this.align
10192         }
10193         if (this.axis) {
10194             cfg.axis=this.axis
10195         }
10196         if (this.bgcolor) {
10197             cfg.bgcolor=this.bgcolor
10198         }
10199         if (this.charoff) {
10200             cfg.charoff=this.charoff
10201         }
10202         if (this.colspan) {
10203             cfg.colspan=this.colspan
10204         }
10205         if (this.headers) {
10206             cfg.headers=this.headers
10207         }
10208         if (this.height) {
10209             cfg.height=this.height
10210         }
10211         if (this.nowrap) {
10212             cfg.nowrap=this.nowrap
10213         }
10214         if (this.rowspan) {
10215             cfg.rowspan=this.rowspan
10216         }
10217         if (this.scope) {
10218             cfg.scope=this.scope
10219         }
10220         if (this.valign) {
10221             cfg.valign=this.valign
10222         }
10223         if (this.width) {
10224             cfg.width=this.width
10225         }
10226         
10227         
10228         return cfg;
10229     }
10230    
10231 });
10232
10233  
10234
10235  /*
10236  * - LGPL
10237  *
10238  * table row
10239  * 
10240  */
10241
10242 /**
10243  * @class Roo.bootstrap.TableRow
10244  * @extends Roo.bootstrap.Component
10245  * Bootstrap TableRow class
10246  * @cfg {String} cls row class
10247  * @cfg {String} align Aligns the content in a table row
10248  * @cfg {String} bgcolor Specifies a background color for a table row
10249  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10250  * @cfg {String} valign Vertical aligns the content in a table row
10251  * 
10252  * @constructor
10253  * Create a new TableRow
10254  * @param {Object} config The config object
10255  */
10256
10257 Roo.bootstrap.TableRow = function(config){
10258     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10259 };
10260
10261 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10262     
10263     cls: false,
10264     align: false,
10265     bgcolor: false,
10266     charoff: false,
10267     valign: false,
10268     
10269     getAutoCreate : function(){
10270         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10271         
10272         cfg = {
10273             tag: 'tr'
10274         };
10275             
10276         if(this.cls){
10277             cfg.cls = this.cls;
10278         }
10279         if(this.align){
10280             cfg.align = this.align;
10281         }
10282         if(this.bgcolor){
10283             cfg.bgcolor = this.bgcolor;
10284         }
10285         if(this.charoff){
10286             cfg.charoff = this.charoff;
10287         }
10288         if(this.valign){
10289             cfg.valign = this.valign;
10290         }
10291         
10292         return cfg;
10293     }
10294    
10295 });
10296
10297  
10298
10299  /*
10300  * - LGPL
10301  *
10302  * table body
10303  * 
10304  */
10305
10306 /**
10307  * @class Roo.bootstrap.TableBody
10308  * @extends Roo.bootstrap.Component
10309  * Bootstrap TableBody class
10310  * @cfg {String} cls element class
10311  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10312  * @cfg {String} align Aligns the content inside the element
10313  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10314  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10315  * 
10316  * @constructor
10317  * Create a new TableBody
10318  * @param {Object} config The config object
10319  */
10320
10321 Roo.bootstrap.TableBody = function(config){
10322     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10323 };
10324
10325 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10326     
10327     cls: false,
10328     tag: false,
10329     align: false,
10330     charoff: false,
10331     valign: false,
10332     
10333     getAutoCreate : function(){
10334         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10335         
10336         cfg = {
10337             tag: 'tbody'
10338         };
10339             
10340         if (this.cls) {
10341             cfg.cls=this.cls
10342         }
10343         if(this.tag){
10344             cfg.tag = this.tag;
10345         }
10346         
10347         if(this.align){
10348             cfg.align = this.align;
10349         }
10350         if(this.charoff){
10351             cfg.charoff = this.charoff;
10352         }
10353         if(this.valign){
10354             cfg.valign = this.valign;
10355         }
10356         
10357         return cfg;
10358     }
10359     
10360     
10361 //    initEvents : function()
10362 //    {
10363 //        
10364 //        if(!this.store){
10365 //            return;
10366 //        }
10367 //        
10368 //        this.store = Roo.factory(this.store, Roo.data);
10369 //        this.store.on('load', this.onLoad, this);
10370 //        
10371 //        this.store.load();
10372 //        
10373 //    },
10374 //    
10375 //    onLoad: function () 
10376 //    {   
10377 //        this.fireEvent('load', this);
10378 //    }
10379 //    
10380 //   
10381 });
10382
10383  
10384
10385  /*
10386  * Based on:
10387  * Ext JS Library 1.1.1
10388  * Copyright(c) 2006-2007, Ext JS, LLC.
10389  *
10390  * Originally Released Under LGPL - original licence link has changed is not relivant.
10391  *
10392  * Fork - LGPL
10393  * <script type="text/javascript">
10394  */
10395
10396 // as we use this in bootstrap.
10397 Roo.namespace('Roo.form');
10398  /**
10399  * @class Roo.form.Action
10400  * Internal Class used to handle form actions
10401  * @constructor
10402  * @param {Roo.form.BasicForm} el The form element or its id
10403  * @param {Object} config Configuration options
10404  */
10405
10406  
10407  
10408 // define the action interface
10409 Roo.form.Action = function(form, options){
10410     this.form = form;
10411     this.options = options || {};
10412 };
10413 /**
10414  * Client Validation Failed
10415  * @const 
10416  */
10417 Roo.form.Action.CLIENT_INVALID = 'client';
10418 /**
10419  * Server Validation Failed
10420  * @const 
10421  */
10422 Roo.form.Action.SERVER_INVALID = 'server';
10423  /**
10424  * Connect to Server Failed
10425  * @const 
10426  */
10427 Roo.form.Action.CONNECT_FAILURE = 'connect';
10428 /**
10429  * Reading Data from Server Failed
10430  * @const 
10431  */
10432 Roo.form.Action.LOAD_FAILURE = 'load';
10433
10434 Roo.form.Action.prototype = {
10435     type : 'default',
10436     failureType : undefined,
10437     response : undefined,
10438     result : undefined,
10439
10440     // interface method
10441     run : function(options){
10442
10443     },
10444
10445     // interface method
10446     success : function(response){
10447
10448     },
10449
10450     // interface method
10451     handleResponse : function(response){
10452
10453     },
10454
10455     // default connection failure
10456     failure : function(response){
10457         
10458         this.response = response;
10459         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10460         this.form.afterAction(this, false);
10461     },
10462
10463     processResponse : function(response){
10464         this.response = response;
10465         if(!response.responseText){
10466             return true;
10467         }
10468         this.result = this.handleResponse(response);
10469         return this.result;
10470     },
10471
10472     // utility functions used internally
10473     getUrl : function(appendParams){
10474         var url = this.options.url || this.form.url || this.form.el.dom.action;
10475         if(appendParams){
10476             var p = this.getParams();
10477             if(p){
10478                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10479             }
10480         }
10481         return url;
10482     },
10483
10484     getMethod : function(){
10485         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10486     },
10487
10488     getParams : function(){
10489         var bp = this.form.baseParams;
10490         var p = this.options.params;
10491         if(p){
10492             if(typeof p == "object"){
10493                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10494             }else if(typeof p == 'string' && bp){
10495                 p += '&' + Roo.urlEncode(bp);
10496             }
10497         }else if(bp){
10498             p = Roo.urlEncode(bp);
10499         }
10500         return p;
10501     },
10502
10503     createCallback : function(){
10504         return {
10505             success: this.success,
10506             failure: this.failure,
10507             scope: this,
10508             timeout: (this.form.timeout*1000),
10509             upload: this.form.fileUpload ? this.success : undefined
10510         };
10511     }
10512 };
10513
10514 Roo.form.Action.Submit = function(form, options){
10515     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10516 };
10517
10518 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10519     type : 'submit',
10520
10521     haveProgress : false,
10522     uploadComplete : false,
10523     
10524     // uploadProgress indicator.
10525     uploadProgress : function()
10526     {
10527         if (!this.form.progressUrl) {
10528             return;
10529         }
10530         
10531         if (!this.haveProgress) {
10532             Roo.MessageBox.progress("Uploading", "Uploading");
10533         }
10534         if (this.uploadComplete) {
10535            Roo.MessageBox.hide();
10536            return;
10537         }
10538         
10539         this.haveProgress = true;
10540    
10541         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10542         
10543         var c = new Roo.data.Connection();
10544         c.request({
10545             url : this.form.progressUrl,
10546             params: {
10547                 id : uid
10548             },
10549             method: 'GET',
10550             success : function(req){
10551                //console.log(data);
10552                 var rdata = false;
10553                 var edata;
10554                 try  {
10555                    rdata = Roo.decode(req.responseText)
10556                 } catch (e) {
10557                     Roo.log("Invalid data from server..");
10558                     Roo.log(edata);
10559                     return;
10560                 }
10561                 if (!rdata || !rdata.success) {
10562                     Roo.log(rdata);
10563                     Roo.MessageBox.alert(Roo.encode(rdata));
10564                     return;
10565                 }
10566                 var data = rdata.data;
10567                 
10568                 if (this.uploadComplete) {
10569                    Roo.MessageBox.hide();
10570                    return;
10571                 }
10572                    
10573                 if (data){
10574                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10575                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10576                     );
10577                 }
10578                 this.uploadProgress.defer(2000,this);
10579             },
10580        
10581             failure: function(data) {
10582                 Roo.log('progress url failed ');
10583                 Roo.log(data);
10584             },
10585             scope : this
10586         });
10587            
10588     },
10589     
10590     
10591     run : function()
10592     {
10593         // run get Values on the form, so it syncs any secondary forms.
10594         this.form.getValues();
10595         
10596         var o = this.options;
10597         var method = this.getMethod();
10598         var isPost = method == 'POST';
10599         if(o.clientValidation === false || this.form.isValid()){
10600             
10601             if (this.form.progressUrl) {
10602                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10603                     (new Date() * 1) + '' + Math.random());
10604                     
10605             } 
10606             
10607             
10608             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10609                 form:this.form.el.dom,
10610                 url:this.getUrl(!isPost),
10611                 method: method,
10612                 params:isPost ? this.getParams() : null,
10613                 isUpload: this.form.fileUpload,
10614                 formData : this.form.formData
10615             }));
10616             
10617             this.uploadProgress();
10618
10619         }else if (o.clientValidation !== false){ // client validation failed
10620             this.failureType = Roo.form.Action.CLIENT_INVALID;
10621             this.form.afterAction(this, false);
10622         }
10623     },
10624
10625     success : function(response)
10626     {
10627         this.uploadComplete= true;
10628         if (this.haveProgress) {
10629             Roo.MessageBox.hide();
10630         }
10631         
10632         
10633         var result = this.processResponse(response);
10634         if(result === true || result.success){
10635             this.form.afterAction(this, true);
10636             return;
10637         }
10638         if(result.errors){
10639             this.form.markInvalid(result.errors);
10640             this.failureType = Roo.form.Action.SERVER_INVALID;
10641         }
10642         this.form.afterAction(this, false);
10643     },
10644     failure : function(response)
10645     {
10646         this.uploadComplete= true;
10647         if (this.haveProgress) {
10648             Roo.MessageBox.hide();
10649         }
10650         
10651         this.response = response;
10652         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10653         this.form.afterAction(this, false);
10654     },
10655     
10656     handleResponse : function(response){
10657         if(this.form.errorReader){
10658             var rs = this.form.errorReader.read(response);
10659             var errors = [];
10660             if(rs.records){
10661                 for(var i = 0, len = rs.records.length; i < len; i++) {
10662                     var r = rs.records[i];
10663                     errors[i] = r.data;
10664                 }
10665             }
10666             if(errors.length < 1){
10667                 errors = null;
10668             }
10669             return {
10670                 success : rs.success,
10671                 errors : errors
10672             };
10673         }
10674         var ret = false;
10675         try {
10676             ret = Roo.decode(response.responseText);
10677         } catch (e) {
10678             ret = {
10679                 success: false,
10680                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10681                 errors : []
10682             };
10683         }
10684         return ret;
10685         
10686     }
10687 });
10688
10689
10690 Roo.form.Action.Load = function(form, options){
10691     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10692     this.reader = this.form.reader;
10693 };
10694
10695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10696     type : 'load',
10697
10698     run : function(){
10699         
10700         Roo.Ajax.request(Roo.apply(
10701                 this.createCallback(), {
10702                     method:this.getMethod(),
10703                     url:this.getUrl(false),
10704                     params:this.getParams()
10705         }));
10706     },
10707
10708     success : function(response){
10709         
10710         var result = this.processResponse(response);
10711         if(result === true || !result.success || !result.data){
10712             this.failureType = Roo.form.Action.LOAD_FAILURE;
10713             this.form.afterAction(this, false);
10714             return;
10715         }
10716         this.form.clearInvalid();
10717         this.form.setValues(result.data);
10718         this.form.afterAction(this, true);
10719     },
10720
10721     handleResponse : function(response){
10722         if(this.form.reader){
10723             var rs = this.form.reader.read(response);
10724             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10725             return {
10726                 success : rs.success,
10727                 data : data
10728             };
10729         }
10730         return Roo.decode(response.responseText);
10731     }
10732 });
10733
10734 Roo.form.Action.ACTION_TYPES = {
10735     'load' : Roo.form.Action.Load,
10736     'submit' : Roo.form.Action.Submit
10737 };/*
10738  * - LGPL
10739  *
10740  * form
10741  *
10742  */
10743
10744 /**
10745  * @class Roo.bootstrap.Form
10746  * @extends Roo.bootstrap.Component
10747  * Bootstrap Form class
10748  * @cfg {String} method  GET | POST (default POST)
10749  * @cfg {String} labelAlign top | left (default top)
10750  * @cfg {String} align left  | right - for navbars
10751  * @cfg {Boolean} loadMask load mask when submit (default true)
10752
10753  *
10754  * @constructor
10755  * Create a new Form
10756  * @param {Object} config The config object
10757  */
10758
10759
10760 Roo.bootstrap.Form = function(config){
10761     
10762     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10763     
10764     Roo.bootstrap.Form.popover.apply();
10765     
10766     this.addEvents({
10767         /**
10768          * @event clientvalidation
10769          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10770          * @param {Form} this
10771          * @param {Boolean} valid true if the form has passed client-side validation
10772          */
10773         clientvalidation: true,
10774         /**
10775          * @event beforeaction
10776          * Fires before any action is performed. Return false to cancel the action.
10777          * @param {Form} this
10778          * @param {Action} action The action to be performed
10779          */
10780         beforeaction: true,
10781         /**
10782          * @event actionfailed
10783          * Fires when an action fails.
10784          * @param {Form} this
10785          * @param {Action} action The action that failed
10786          */
10787         actionfailed : true,
10788         /**
10789          * @event actioncomplete
10790          * Fires when an action is completed.
10791          * @param {Form} this
10792          * @param {Action} action The action that completed
10793          */
10794         actioncomplete : true
10795     });
10796 };
10797
10798 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10799
10800      /**
10801      * @cfg {String} method
10802      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10803      */
10804     method : 'POST',
10805     /**
10806      * @cfg {String} url
10807      * The URL to use for form actions if one isn't supplied in the action options.
10808      */
10809     /**
10810      * @cfg {Boolean} fileUpload
10811      * Set to true if this form is a file upload.
10812      */
10813
10814     /**
10815      * @cfg {Object} baseParams
10816      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10817      */
10818
10819     /**
10820      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10821      */
10822     timeout: 30,
10823     /**
10824      * @cfg {Sting} align (left|right) for navbar forms
10825      */
10826     align : 'left',
10827
10828     // private
10829     activeAction : null,
10830
10831     /**
10832      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10833      * element by passing it or its id or mask the form itself by passing in true.
10834      * @type Mixed
10835      */
10836     waitMsgTarget : false,
10837
10838     loadMask : true,
10839     
10840     /**
10841      * @cfg {Boolean} errorMask (true|false) default false
10842      */
10843     errorMask : false,
10844     
10845     /**
10846      * @cfg {Number} maskOffset Default 100
10847      */
10848     maskOffset : 100,
10849     
10850     /**
10851      * @cfg {Boolean} maskBody
10852      */
10853     maskBody : false,
10854
10855     getAutoCreate : function(){
10856
10857         var cfg = {
10858             tag: 'form',
10859             method : this.method || 'POST',
10860             id : this.id || Roo.id(),
10861             cls : ''
10862         };
10863         if (this.parent().xtype.match(/^Nav/)) {
10864             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10865
10866         }
10867
10868         if (this.labelAlign == 'left' ) {
10869             cfg.cls += ' form-horizontal';
10870         }
10871
10872
10873         return cfg;
10874     },
10875     initEvents : function()
10876     {
10877         this.el.on('submit', this.onSubmit, this);
10878         // this was added as random key presses on the form where triggering form submit.
10879         this.el.on('keypress', function(e) {
10880             if (e.getCharCode() != 13) {
10881                 return true;
10882             }
10883             // we might need to allow it for textareas.. and some other items.
10884             // check e.getTarget().
10885
10886             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10887                 return true;
10888             }
10889
10890             Roo.log("keypress blocked");
10891
10892             e.preventDefault();
10893             return false;
10894         });
10895         
10896     },
10897     // private
10898     onSubmit : function(e){
10899         e.stopEvent();
10900     },
10901
10902      /**
10903      * Returns true if client-side validation on the form is successful.
10904      * @return Boolean
10905      */
10906     isValid : function(){
10907         var items = this.getItems();
10908         var valid = true;
10909         var target = false;
10910         
10911         items.each(function(f){
10912             
10913             if(f.validate()){
10914                 return;
10915             }
10916             
10917             Roo.log('invalid field: ' + f.name);
10918             
10919             valid = false;
10920
10921             if(!target && f.el.isVisible(true)){
10922                 target = f;
10923             }
10924            
10925         });
10926         
10927         if(this.errorMask && !valid){
10928             Roo.bootstrap.Form.popover.mask(this, target);
10929         }
10930         
10931         return valid;
10932     },
10933     
10934     /**
10935      * Returns true if any fields in this form have changed since their original load.
10936      * @return Boolean
10937      */
10938     isDirty : function(){
10939         var dirty = false;
10940         var items = this.getItems();
10941         items.each(function(f){
10942            if(f.isDirty()){
10943                dirty = true;
10944                return false;
10945            }
10946            return true;
10947         });
10948         return dirty;
10949     },
10950      /**
10951      * Performs a predefined action (submit or load) or custom actions you define on this form.
10952      * @param {String} actionName The name of the action type
10953      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10954      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10955      * accept other config options):
10956      * <pre>
10957 Property          Type             Description
10958 ----------------  ---------------  ----------------------------------------------------------------------------------
10959 url               String           The url for the action (defaults to the form's url)
10960 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10961 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10962 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10963                                    validate the form on the client (defaults to false)
10964      * </pre>
10965      * @return {BasicForm} this
10966      */
10967     doAction : function(action, options){
10968         if(typeof action == 'string'){
10969             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10970         }
10971         if(this.fireEvent('beforeaction', this, action) !== false){
10972             this.beforeAction(action);
10973             action.run.defer(100, action);
10974         }
10975         return this;
10976     },
10977
10978     // private
10979     beforeAction : function(action){
10980         var o = action.options;
10981         
10982         if(this.loadMask){
10983             
10984             if(this.maskBody){
10985                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10986             } else {
10987                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10988             }
10989         }
10990         // not really supported yet.. ??
10991
10992         //if(this.waitMsgTarget === true){
10993         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10994         //}else if(this.waitMsgTarget){
10995         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10996         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10997         //}else {
10998         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10999        // }
11000
11001     },
11002
11003     // private
11004     afterAction : function(action, success){
11005         this.activeAction = null;
11006         var o = action.options;
11007
11008         if(this.loadMask){
11009             
11010             if(this.maskBody){
11011                 Roo.get(document.body).unmask();
11012             } else {
11013                 this.el.unmask();
11014             }
11015         }
11016         
11017         //if(this.waitMsgTarget === true){
11018 //            this.el.unmask();
11019         //}else if(this.waitMsgTarget){
11020         //    this.waitMsgTarget.unmask();
11021         //}else{
11022         //    Roo.MessageBox.updateProgress(1);
11023         //    Roo.MessageBox.hide();
11024        // }
11025         //
11026         if(success){
11027             if(o.reset){
11028                 this.reset();
11029             }
11030             Roo.callback(o.success, o.scope, [this, action]);
11031             this.fireEvent('actioncomplete', this, action);
11032
11033         }else{
11034
11035             // failure condition..
11036             // we have a scenario where updates need confirming.
11037             // eg. if a locking scenario exists..
11038             // we look for { errors : { needs_confirm : true }} in the response.
11039             if (
11040                 (typeof(action.result) != 'undefined')  &&
11041                 (typeof(action.result.errors) != 'undefined')  &&
11042                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11043            ){
11044                 var _t = this;
11045                 Roo.log("not supported yet");
11046                  /*
11047
11048                 Roo.MessageBox.confirm(
11049                     "Change requires confirmation",
11050                     action.result.errorMsg,
11051                     function(r) {
11052                         if (r != 'yes') {
11053                             return;
11054                         }
11055                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11056                     }
11057
11058                 );
11059                 */
11060
11061
11062                 return;
11063             }
11064
11065             Roo.callback(o.failure, o.scope, [this, action]);
11066             // show an error message if no failed handler is set..
11067             if (!this.hasListener('actionfailed')) {
11068                 Roo.log("need to add dialog support");
11069                 /*
11070                 Roo.MessageBox.alert("Error",
11071                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11072                         action.result.errorMsg :
11073                         "Saving Failed, please check your entries or try again"
11074                 );
11075                 */
11076             }
11077
11078             this.fireEvent('actionfailed', this, action);
11079         }
11080
11081     },
11082     /**
11083      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11084      * @param {String} id The value to search for
11085      * @return Field
11086      */
11087     findField : function(id){
11088         var items = this.getItems();
11089         var field = items.get(id);
11090         if(!field){
11091              items.each(function(f){
11092                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11093                     field = f;
11094                     return false;
11095                 }
11096                 return true;
11097             });
11098         }
11099         return field || null;
11100     },
11101      /**
11102      * Mark fields in this form invalid in bulk.
11103      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11104      * @return {BasicForm} this
11105      */
11106     markInvalid : function(errors){
11107         if(errors instanceof Array){
11108             for(var i = 0, len = errors.length; i < len; i++){
11109                 var fieldError = errors[i];
11110                 var f = this.findField(fieldError.id);
11111                 if(f){
11112                     f.markInvalid(fieldError.msg);
11113                 }
11114             }
11115         }else{
11116             var field, id;
11117             for(id in errors){
11118                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11119                     field.markInvalid(errors[id]);
11120                 }
11121             }
11122         }
11123         //Roo.each(this.childForms || [], function (f) {
11124         //    f.markInvalid(errors);
11125         //});
11126
11127         return this;
11128     },
11129
11130     /**
11131      * Set values for fields in this form in bulk.
11132      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11133      * @return {BasicForm} this
11134      */
11135     setValues : function(values){
11136         if(values instanceof Array){ // array of objects
11137             for(var i = 0, len = values.length; i < len; i++){
11138                 var v = values[i];
11139                 var f = this.findField(v.id);
11140                 if(f){
11141                     f.setValue(v.value);
11142                     if(this.trackResetOnLoad){
11143                         f.originalValue = f.getValue();
11144                     }
11145                 }
11146             }
11147         }else{ // object hash
11148             var field, id;
11149             for(id in values){
11150                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11151
11152                     if (field.setFromData &&
11153                         field.valueField &&
11154                         field.displayField &&
11155                         // combos' with local stores can
11156                         // be queried via setValue()
11157                         // to set their value..
11158                         (field.store && !field.store.isLocal)
11159                         ) {
11160                         // it's a combo
11161                         var sd = { };
11162                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11163                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11164                         field.setFromData(sd);
11165
11166                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11167                         
11168                         field.setFromData(values);
11169                         
11170                     } else {
11171                         field.setValue(values[id]);
11172                     }
11173
11174
11175                     if(this.trackResetOnLoad){
11176                         field.originalValue = field.getValue();
11177                     }
11178                 }
11179             }
11180         }
11181
11182         //Roo.each(this.childForms || [], function (f) {
11183         //    f.setValues(values);
11184         //});
11185
11186         return this;
11187     },
11188
11189     /**
11190      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11191      * they are returned as an array.
11192      * @param {Boolean} asString
11193      * @return {Object}
11194      */
11195     getValues : function(asString){
11196         //if (this.childForms) {
11197             // copy values from the child forms
11198         //    Roo.each(this.childForms, function (f) {
11199         //        this.setValues(f.getValues());
11200         //    }, this);
11201         //}
11202
11203
11204
11205         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11206         if(asString === true){
11207             return fs;
11208         }
11209         return Roo.urlDecode(fs);
11210     },
11211
11212     /**
11213      * Returns the fields in this form as an object with key/value pairs.
11214      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11215      * @return {Object}
11216      */
11217     getFieldValues : function(with_hidden)
11218     {
11219         var items = this.getItems();
11220         var ret = {};
11221         items.each(function(f){
11222             
11223             if (!f.getName()) {
11224                 return;
11225             }
11226             
11227             var v = f.getValue();
11228             
11229             if (f.inputType =='radio') {
11230                 if (typeof(ret[f.getName()]) == 'undefined') {
11231                     ret[f.getName()] = ''; // empty..
11232                 }
11233
11234                 if (!f.el.dom.checked) {
11235                     return;
11236
11237                 }
11238                 v = f.el.dom.value;
11239
11240             }
11241             
11242             if(f.xtype == 'MoneyField'){
11243                 ret[f.currencyName] = f.getCurrency();
11244             }
11245
11246             // not sure if this supported any more..
11247             if ((typeof(v) == 'object') && f.getRawValue) {
11248                 v = f.getRawValue() ; // dates..
11249             }
11250             // combo boxes where name != hiddenName...
11251             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11252                 ret[f.name] = f.getRawValue();
11253             }
11254             ret[f.getName()] = v;
11255         });
11256
11257         return ret;
11258     },
11259
11260     /**
11261      * Clears all invalid messages in this form.
11262      * @return {BasicForm} this
11263      */
11264     clearInvalid : function(){
11265         var items = this.getItems();
11266
11267         items.each(function(f){
11268            f.clearInvalid();
11269         });
11270
11271         return this;
11272     },
11273
11274     /**
11275      * Resets this form.
11276      * @return {BasicForm} this
11277      */
11278     reset : function(){
11279         var items = this.getItems();
11280         items.each(function(f){
11281             f.reset();
11282         });
11283
11284         Roo.each(this.childForms || [], function (f) {
11285             f.reset();
11286         });
11287
11288
11289         return this;
11290     },
11291     
11292     getItems : function()
11293     {
11294         var r=new Roo.util.MixedCollection(false, function(o){
11295             return o.id || (o.id = Roo.id());
11296         });
11297         var iter = function(el) {
11298             if (el.inputEl) {
11299                 r.add(el);
11300             }
11301             if (!el.items) {
11302                 return;
11303             }
11304             Roo.each(el.items,function(e) {
11305                 iter(e);
11306             });
11307         };
11308
11309         iter(this);
11310         return r;
11311     },
11312     
11313     hideFields : function(items)
11314     {
11315         Roo.each(items, function(i){
11316             
11317             var f = this.findField(i);
11318             
11319             if(!f){
11320                 return;
11321             }
11322             
11323             f.hide();
11324             
11325         }, this);
11326     },
11327     
11328     showFields : function(items)
11329     {
11330         Roo.each(items, function(i){
11331             
11332             var f = this.findField(i);
11333             
11334             if(!f){
11335                 return;
11336             }
11337             
11338             f.show();
11339             
11340         }, this);
11341     }
11342
11343 });
11344
11345 Roo.apply(Roo.bootstrap.Form, {
11346     
11347     popover : {
11348         
11349         padding : 5,
11350         
11351         isApplied : false,
11352         
11353         isMasked : false,
11354         
11355         form : false,
11356         
11357         target : false,
11358         
11359         toolTip : false,
11360         
11361         intervalID : false,
11362         
11363         maskEl : false,
11364         
11365         apply : function()
11366         {
11367             if(this.isApplied){
11368                 return;
11369             }
11370             
11371             this.maskEl = {
11372                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11373                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11374                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11375                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11376             };
11377             
11378             this.maskEl.top.enableDisplayMode("block");
11379             this.maskEl.left.enableDisplayMode("block");
11380             this.maskEl.bottom.enableDisplayMode("block");
11381             this.maskEl.right.enableDisplayMode("block");
11382             
11383             this.toolTip = new Roo.bootstrap.Tooltip({
11384                 cls : 'roo-form-error-popover',
11385                 alignment : {
11386                     'left' : ['r-l', [-2,0], 'right'],
11387                     'right' : ['l-r', [2,0], 'left'],
11388                     'bottom' : ['tl-bl', [0,2], 'top'],
11389                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11390                 }
11391             });
11392             
11393             this.toolTip.render(Roo.get(document.body));
11394
11395             this.toolTip.el.enableDisplayMode("block");
11396             
11397             Roo.get(document.body).on('click', function(){
11398                 this.unmask();
11399             }, this);
11400             
11401             Roo.get(document.body).on('touchstart', function(){
11402                 this.unmask();
11403             }, this);
11404             
11405             this.isApplied = true
11406         },
11407         
11408         mask : function(form, target)
11409         {
11410             this.form = form;
11411             
11412             this.target = target;
11413             
11414             if(!this.form.errorMask || !target.el){
11415                 return;
11416             }
11417             
11418             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11419             
11420             Roo.log(scrollable);
11421             
11422             var ot = this.target.el.calcOffsetsTo(scrollable);
11423             
11424             var scrollTo = ot[1] - this.form.maskOffset;
11425             
11426             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11427             
11428             scrollable.scrollTo('top', scrollTo);
11429             
11430             var box = this.target.el.getBox();
11431             Roo.log(box);
11432             var zIndex = Roo.bootstrap.Modal.zIndex++;
11433
11434             
11435             this.maskEl.top.setStyle('position', 'absolute');
11436             this.maskEl.top.setStyle('z-index', zIndex);
11437             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11438             this.maskEl.top.setLeft(0);
11439             this.maskEl.top.setTop(0);
11440             this.maskEl.top.show();
11441             
11442             this.maskEl.left.setStyle('position', 'absolute');
11443             this.maskEl.left.setStyle('z-index', zIndex);
11444             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11445             this.maskEl.left.setLeft(0);
11446             this.maskEl.left.setTop(box.y - this.padding);
11447             this.maskEl.left.show();
11448
11449             this.maskEl.bottom.setStyle('position', 'absolute');
11450             this.maskEl.bottom.setStyle('z-index', zIndex);
11451             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11452             this.maskEl.bottom.setLeft(0);
11453             this.maskEl.bottom.setTop(box.bottom + this.padding);
11454             this.maskEl.bottom.show();
11455
11456             this.maskEl.right.setStyle('position', 'absolute');
11457             this.maskEl.right.setStyle('z-index', zIndex);
11458             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11459             this.maskEl.right.setLeft(box.right + this.padding);
11460             this.maskEl.right.setTop(box.y - this.padding);
11461             this.maskEl.right.show();
11462
11463             this.toolTip.bindEl = this.target.el;
11464
11465             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11466
11467             var tip = this.target.blankText;
11468
11469             if(this.target.getValue() !== '' ) {
11470                 
11471                 if (this.target.invalidText.length) {
11472                     tip = this.target.invalidText;
11473                 } else if (this.target.regexText.length){
11474                     tip = this.target.regexText;
11475                 }
11476             }
11477
11478             this.toolTip.show(tip);
11479
11480             this.intervalID = window.setInterval(function() {
11481                 Roo.bootstrap.Form.popover.unmask();
11482             }, 10000);
11483
11484             window.onwheel = function(){ return false;};
11485             
11486             (function(){ this.isMasked = true; }).defer(500, this);
11487             
11488         },
11489         
11490         unmask : function()
11491         {
11492             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11493                 return;
11494             }
11495             
11496             this.maskEl.top.setStyle('position', 'absolute');
11497             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11498             this.maskEl.top.hide();
11499
11500             this.maskEl.left.setStyle('position', 'absolute');
11501             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11502             this.maskEl.left.hide();
11503
11504             this.maskEl.bottom.setStyle('position', 'absolute');
11505             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11506             this.maskEl.bottom.hide();
11507
11508             this.maskEl.right.setStyle('position', 'absolute');
11509             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11510             this.maskEl.right.hide();
11511             
11512             this.toolTip.hide();
11513             
11514             this.toolTip.el.hide();
11515             
11516             window.onwheel = function(){ return true;};
11517             
11518             if(this.intervalID){
11519                 window.clearInterval(this.intervalID);
11520                 this.intervalID = false;
11521             }
11522             
11523             this.isMasked = false;
11524             
11525         }
11526         
11527     }
11528     
11529 });
11530
11531 /*
11532  * Based on:
11533  * Ext JS Library 1.1.1
11534  * Copyright(c) 2006-2007, Ext JS, LLC.
11535  *
11536  * Originally Released Under LGPL - original licence link has changed is not relivant.
11537  *
11538  * Fork - LGPL
11539  * <script type="text/javascript">
11540  */
11541 /**
11542  * @class Roo.form.VTypes
11543  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11544  * @singleton
11545  */
11546 Roo.form.VTypes = function(){
11547     // closure these in so they are only created once.
11548     var alpha = /^[a-zA-Z_]+$/;
11549     var alphanum = /^[a-zA-Z0-9_]+$/;
11550     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11551     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11552
11553     // All these messages and functions are configurable
11554     return {
11555         /**
11556          * The function used to validate email addresses
11557          * @param {String} value The email address
11558          */
11559         'email' : function(v){
11560             return email.test(v);
11561         },
11562         /**
11563          * The error text to display when the email validation function returns false
11564          * @type String
11565          */
11566         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11567         /**
11568          * The keystroke filter mask to be applied on email input
11569          * @type RegExp
11570          */
11571         'emailMask' : /[a-z0-9_\.\-@]/i,
11572
11573         /**
11574          * The function used to validate URLs
11575          * @param {String} value The URL
11576          */
11577         'url' : function(v){
11578             return url.test(v);
11579         },
11580         /**
11581          * The error text to display when the url validation function returns false
11582          * @type String
11583          */
11584         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11585         
11586         /**
11587          * The function used to validate alpha values
11588          * @param {String} value The value
11589          */
11590         'alpha' : function(v){
11591             return alpha.test(v);
11592         },
11593         /**
11594          * The error text to display when the alpha validation function returns false
11595          * @type String
11596          */
11597         'alphaText' : 'This field should only contain letters and _',
11598         /**
11599          * The keystroke filter mask to be applied on alpha input
11600          * @type RegExp
11601          */
11602         'alphaMask' : /[a-z_]/i,
11603
11604         /**
11605          * The function used to validate alphanumeric values
11606          * @param {String} value The value
11607          */
11608         'alphanum' : function(v){
11609             return alphanum.test(v);
11610         },
11611         /**
11612          * The error text to display when the alphanumeric validation function returns false
11613          * @type String
11614          */
11615         'alphanumText' : 'This field should only contain letters, numbers and _',
11616         /**
11617          * The keystroke filter mask to be applied on alphanumeric input
11618          * @type RegExp
11619          */
11620         'alphanumMask' : /[a-z0-9_]/i
11621     };
11622 }();/*
11623  * - LGPL
11624  *
11625  * Input
11626  * 
11627  */
11628
11629 /**
11630  * @class Roo.bootstrap.Input
11631  * @extends Roo.bootstrap.Component
11632  * Bootstrap Input class
11633  * @cfg {Boolean} disabled is it disabled
11634  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11635  * @cfg {String} name name of the input
11636  * @cfg {string} fieldLabel - the label associated
11637  * @cfg {string} placeholder - placeholder to put in text.
11638  * @cfg {string}  before - input group add on before
11639  * @cfg {string} after - input group add on after
11640  * @cfg {string} size - (lg|sm) or leave empty..
11641  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11642  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11643  * @cfg {Number} md colspan out of 12 for computer-sized screens
11644  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11645  * @cfg {string} value default value of the input
11646  * @cfg {Number} labelWidth set the width of label 
11647  * @cfg {Number} labellg set the width of label (1-12)
11648  * @cfg {Number} labelmd set the width of label (1-12)
11649  * @cfg {Number} labelsm set the width of label (1-12)
11650  * @cfg {Number} labelxs set the width of label (1-12)
11651  * @cfg {String} labelAlign (top|left)
11652  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11653  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11654  * @cfg {String} indicatorpos (left|right) default left
11655  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11656  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11657  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11658
11659  * @cfg {String} align (left|center|right) Default left
11660  * @cfg {Boolean} forceFeedback (true|false) Default false
11661  * 
11662  * @constructor
11663  * Create a new Input
11664  * @param {Object} config The config object
11665  */
11666
11667 Roo.bootstrap.Input = function(config){
11668     
11669     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11670     
11671     this.addEvents({
11672         /**
11673          * @event focus
11674          * Fires when this field receives input focus.
11675          * @param {Roo.form.Field} this
11676          */
11677         focus : true,
11678         /**
11679          * @event blur
11680          * Fires when this field loses input focus.
11681          * @param {Roo.form.Field} this
11682          */
11683         blur : true,
11684         /**
11685          * @event specialkey
11686          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11687          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11688          * @param {Roo.form.Field} this
11689          * @param {Roo.EventObject} e The event object
11690          */
11691         specialkey : true,
11692         /**
11693          * @event change
11694          * Fires just before the field blurs if the field value has changed.
11695          * @param {Roo.form.Field} this
11696          * @param {Mixed} newValue The new value
11697          * @param {Mixed} oldValue The original value
11698          */
11699         change : true,
11700         /**
11701          * @event invalid
11702          * Fires after the field has been marked as invalid.
11703          * @param {Roo.form.Field} this
11704          * @param {String} msg The validation message
11705          */
11706         invalid : true,
11707         /**
11708          * @event valid
11709          * Fires after the field has been validated with no errors.
11710          * @param {Roo.form.Field} this
11711          */
11712         valid : true,
11713          /**
11714          * @event keyup
11715          * Fires after the key up
11716          * @param {Roo.form.Field} this
11717          * @param {Roo.EventObject}  e The event Object
11718          */
11719         keyup : true,
11720         /**
11721          * @event paste
11722          * Fires after the user pastes into input
11723          * @param {Roo.form.Field} this
11724          * @param {Roo.EventObject}  e The event Object
11725          */
11726         paste : true
11727     });
11728 };
11729
11730 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11731      /**
11732      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11733       automatic validation (defaults to "keyup").
11734      */
11735     validationEvent : "keyup",
11736      /**
11737      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11738      */
11739     validateOnBlur : true,
11740     /**
11741      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11742      */
11743     validationDelay : 250,
11744      /**
11745      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11746      */
11747     focusClass : "x-form-focus",  // not needed???
11748     
11749        
11750     /**
11751      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11752      */
11753     invalidClass : "has-warning",
11754     
11755     /**
11756      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11757      */
11758     validClass : "has-success",
11759     
11760     /**
11761      * @cfg {Boolean} hasFeedback (true|false) default true
11762      */
11763     hasFeedback : true,
11764     
11765     /**
11766      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11767      */
11768     invalidFeedbackClass : "glyphicon-warning-sign",
11769     
11770     /**
11771      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11772      */
11773     validFeedbackClass : "glyphicon-ok",
11774     
11775     /**
11776      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11777      */
11778     selectOnFocus : false,
11779     
11780      /**
11781      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11782      */
11783     maskRe : null,
11784        /**
11785      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11786      */
11787     vtype : null,
11788     
11789       /**
11790      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11791      */
11792     disableKeyFilter : false,
11793     
11794        /**
11795      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11796      */
11797     disabled : false,
11798      /**
11799      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11800      */
11801     allowBlank : true,
11802     /**
11803      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11804      */
11805     blankText : "Please complete this mandatory field",
11806     
11807      /**
11808      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11809      */
11810     minLength : 0,
11811     /**
11812      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11813      */
11814     maxLength : Number.MAX_VALUE,
11815     /**
11816      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11817      */
11818     minLengthText : "The minimum length for this field is {0}",
11819     /**
11820      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11821      */
11822     maxLengthText : "The maximum length for this field is {0}",
11823   
11824     
11825     /**
11826      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11827      * If available, this function will be called only after the basic validators all return true, and will be passed the
11828      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11829      */
11830     validator : null,
11831     /**
11832      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11833      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11834      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11835      */
11836     regex : null,
11837     /**
11838      * @cfg {String} regexText -- Depricated - use Invalid Text
11839      */
11840     regexText : "",
11841     
11842     /**
11843      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11844      */
11845     invalidText : "",
11846     
11847     
11848     
11849     autocomplete: false,
11850     
11851     
11852     fieldLabel : '',
11853     inputType : 'text',
11854     
11855     name : false,
11856     placeholder: false,
11857     before : false,
11858     after : false,
11859     size : false,
11860     hasFocus : false,
11861     preventMark: false,
11862     isFormField : true,
11863     value : '',
11864     labelWidth : 2,
11865     labelAlign : false,
11866     readOnly : false,
11867     align : false,
11868     formatedValue : false,
11869     forceFeedback : false,
11870     
11871     indicatorpos : 'left',
11872     
11873     labellg : 0,
11874     labelmd : 0,
11875     labelsm : 0,
11876     labelxs : 0,
11877     
11878     capture : '',
11879     accept : '',
11880     
11881     parentLabelAlign : function()
11882     {
11883         var parent = this;
11884         while (parent.parent()) {
11885             parent = parent.parent();
11886             if (typeof(parent.labelAlign) !='undefined') {
11887                 return parent.labelAlign;
11888             }
11889         }
11890         return 'left';
11891         
11892     },
11893     
11894     getAutoCreate : function()
11895     {
11896         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11897         
11898         var id = Roo.id();
11899         
11900         var cfg = {};
11901         
11902         if(this.inputType != 'hidden'){
11903             cfg.cls = 'form-group' //input-group
11904         }
11905         
11906         var input =  {
11907             tag: 'input',
11908             id : id,
11909             type : this.inputType,
11910             value : this.value,
11911             cls : 'form-control',
11912             placeholder : this.placeholder || '',
11913             autocomplete : this.autocomplete || 'new-password'
11914         };
11915         if (this.inputType == 'file') {
11916             input.style = 'overflow:hidden'; // why not in CSS?
11917         }
11918         
11919         if(this.capture.length){
11920             input.capture = this.capture;
11921         }
11922         
11923         if(this.accept.length){
11924             input.accept = this.accept + "/*";
11925         }
11926         
11927         if(this.align){
11928             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11929         }
11930         
11931         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11932             input.maxLength = this.maxLength;
11933         }
11934         
11935         if (this.disabled) {
11936             input.disabled=true;
11937         }
11938         
11939         if (this.readOnly) {
11940             input.readonly=true;
11941         }
11942         
11943         if (this.name) {
11944             input.name = this.name;
11945         }
11946         
11947         if (this.size) {
11948             input.cls += ' input-' + this.size;
11949         }
11950         
11951         var settings=this;
11952         ['xs','sm','md','lg'].map(function(size){
11953             if (settings[size]) {
11954                 cfg.cls += ' col-' + size + '-' + settings[size];
11955             }
11956         });
11957         
11958         var inputblock = input;
11959         
11960         var feedback = {
11961             tag: 'span',
11962             cls: 'glyphicon form-control-feedback'
11963         };
11964             
11965         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11966             
11967             inputblock = {
11968                 cls : 'has-feedback',
11969                 cn :  [
11970                     input,
11971                     feedback
11972                 ] 
11973             };  
11974         }
11975         
11976         if (this.before || this.after) {
11977             
11978             inputblock = {
11979                 cls : 'input-group',
11980                 cn :  [] 
11981             };
11982             
11983             if (this.before && typeof(this.before) == 'string') {
11984                 
11985                 inputblock.cn.push({
11986                     tag :'span',
11987                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11988                     html : this.before
11989                 });
11990             }
11991             if (this.before && typeof(this.before) == 'object') {
11992                 this.before = Roo.factory(this.before);
11993                 
11994                 inputblock.cn.push({
11995                     tag :'span',
11996                     cls : 'roo-input-before input-group-prepend   input-group-' +
11997                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11998                 });
11999             }
12000             
12001             inputblock.cn.push(input);
12002             
12003             if (this.after && typeof(this.after) == 'string') {
12004                 inputblock.cn.push({
12005                     tag :'span',
12006                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12007                     html : this.after
12008                 });
12009             }
12010             if (this.after && typeof(this.after) == 'object') {
12011                 this.after = Roo.factory(this.after);
12012                 
12013                 inputblock.cn.push({
12014                     tag :'span',
12015                     cls : 'roo-input-after input-group-append  input-group-' +
12016                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12017                 });
12018             }
12019             
12020             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12021                 inputblock.cls += ' has-feedback';
12022                 inputblock.cn.push(feedback);
12023             }
12024         };
12025         var indicator = {
12026             tag : 'i',
12027             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12028             tooltip : 'This field is required'
12029         };
12030         if (this.allowBlank ) {
12031             indicator.style = this.allowBlank ? ' display:none' : '';
12032         }
12033         if (align ==='left' && this.fieldLabel.length) {
12034             
12035             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12036             
12037             cfg.cn = [
12038                 indicator,
12039                 {
12040                     tag: 'label',
12041                     'for' :  id,
12042                     cls : 'control-label col-form-label',
12043                     html : this.fieldLabel
12044
12045                 },
12046                 {
12047                     cls : "", 
12048                     cn: [
12049                         inputblock
12050                     ]
12051                 }
12052             ];
12053             
12054             var labelCfg = cfg.cn[1];
12055             var contentCfg = cfg.cn[2];
12056             
12057             if(this.indicatorpos == 'right'){
12058                 cfg.cn = [
12059                     {
12060                         tag: 'label',
12061                         'for' :  id,
12062                         cls : 'control-label col-form-label',
12063                         cn : [
12064                             {
12065                                 tag : 'span',
12066                                 html : this.fieldLabel
12067                             },
12068                             indicator
12069                         ]
12070                     },
12071                     {
12072                         cls : "",
12073                         cn: [
12074                             inputblock
12075                         ]
12076                     }
12077
12078                 ];
12079                 
12080                 labelCfg = cfg.cn[0];
12081                 contentCfg = cfg.cn[1];
12082             
12083             }
12084             
12085             if(this.labelWidth > 12){
12086                 labelCfg.style = "width: " + this.labelWidth + 'px';
12087             }
12088             
12089             if(this.labelWidth < 13 && this.labelmd == 0){
12090                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12091             }
12092             
12093             if(this.labellg > 0){
12094                 labelCfg.cls += ' col-lg-' + this.labellg;
12095                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12096             }
12097             
12098             if(this.labelmd > 0){
12099                 labelCfg.cls += ' col-md-' + this.labelmd;
12100                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12101             }
12102             
12103             if(this.labelsm > 0){
12104                 labelCfg.cls += ' col-sm-' + this.labelsm;
12105                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12106             }
12107             
12108             if(this.labelxs > 0){
12109                 labelCfg.cls += ' col-xs-' + this.labelxs;
12110                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12111             }
12112             
12113             
12114         } else if ( this.fieldLabel.length) {
12115                 
12116             
12117             
12118             cfg.cn = [
12119                 {
12120                     tag : 'i',
12121                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12122                     tooltip : 'This field is required',
12123                     style : this.allowBlank ? ' display:none' : '' 
12124                 },
12125                 {
12126                     tag: 'label',
12127                    //cls : 'input-group-addon',
12128                     html : this.fieldLabel
12129
12130                 },
12131
12132                inputblock
12133
12134            ];
12135            
12136            if(this.indicatorpos == 'right'){
12137        
12138                 cfg.cn = [
12139                     {
12140                         tag: 'label',
12141                        //cls : 'input-group-addon',
12142                         html : this.fieldLabel
12143
12144                     },
12145                     {
12146                         tag : 'i',
12147                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12148                         tooltip : 'This field is required',
12149                         style : this.allowBlank ? ' display:none' : '' 
12150                     },
12151
12152                    inputblock
12153
12154                ];
12155
12156             }
12157
12158         } else {
12159             
12160             cfg.cn = [
12161
12162                     inputblock
12163
12164             ];
12165                 
12166                 
12167         };
12168         
12169         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12170            cfg.cls += ' navbar-form';
12171         }
12172         
12173         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12174             // on BS4 we do this only if not form 
12175             cfg.cls += ' navbar-form';
12176             cfg.tag = 'li';
12177         }
12178         
12179         return cfg;
12180         
12181     },
12182     /**
12183      * return the real input element.
12184      */
12185     inputEl: function ()
12186     {
12187         return this.el.select('input.form-control',true).first();
12188     },
12189     
12190     tooltipEl : function()
12191     {
12192         return this.inputEl();
12193     },
12194     
12195     indicatorEl : function()
12196     {
12197         if (Roo.bootstrap.version == 4) {
12198             return false; // not enabled in v4 yet.
12199         }
12200         
12201         var indicator = this.el.select('i.roo-required-indicator',true).first();
12202         
12203         if(!indicator){
12204             return false;
12205         }
12206         
12207         return indicator;
12208         
12209     },
12210     
12211     setDisabled : function(v)
12212     {
12213         var i  = this.inputEl().dom;
12214         if (!v) {
12215             i.removeAttribute('disabled');
12216             return;
12217             
12218         }
12219         i.setAttribute('disabled','true');
12220     },
12221     initEvents : function()
12222     {
12223           
12224         this.inputEl().on("keydown" , this.fireKey,  this);
12225         this.inputEl().on("focus", this.onFocus,  this);
12226         this.inputEl().on("blur", this.onBlur,  this);
12227         
12228         this.inputEl().relayEvent('keyup', this);
12229         this.inputEl().relayEvent('paste', this);
12230         
12231         this.indicator = this.indicatorEl();
12232         
12233         if(this.indicator){
12234             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12235         }
12236  
12237         // reference to original value for reset
12238         this.originalValue = this.getValue();
12239         //Roo.form.TextField.superclass.initEvents.call(this);
12240         if(this.validationEvent == 'keyup'){
12241             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12242             this.inputEl().on('keyup', this.filterValidation, this);
12243         }
12244         else if(this.validationEvent !== false){
12245             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12246         }
12247         
12248         if(this.selectOnFocus){
12249             this.on("focus", this.preFocus, this);
12250             
12251         }
12252         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12253             this.inputEl().on("keypress", this.filterKeys, this);
12254         } else {
12255             this.inputEl().relayEvent('keypress', this);
12256         }
12257        /* if(this.grow){
12258             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12259             this.el.on("click", this.autoSize,  this);
12260         }
12261         */
12262         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12263             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12264         }
12265         
12266         if (typeof(this.before) == 'object') {
12267             this.before.render(this.el.select('.roo-input-before',true).first());
12268         }
12269         if (typeof(this.after) == 'object') {
12270             this.after.render(this.el.select('.roo-input-after',true).first());
12271         }
12272         
12273         this.inputEl().on('change', this.onChange, this);
12274         
12275     },
12276     filterValidation : function(e){
12277         if(!e.isNavKeyPress()){
12278             this.validationTask.delay(this.validationDelay);
12279         }
12280     },
12281      /**
12282      * Validates the field value
12283      * @return {Boolean} True if the value is valid, else false
12284      */
12285     validate : function(){
12286         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12287         if(this.disabled || this.validateValue(this.getRawValue())){
12288             this.markValid();
12289             return true;
12290         }
12291         
12292         this.markInvalid();
12293         return false;
12294     },
12295     
12296     
12297     /**
12298      * Validates a value according to the field's validation rules and marks the field as invalid
12299      * if the validation fails
12300      * @param {Mixed} value The value to validate
12301      * @return {Boolean} True if the value is valid, else false
12302      */
12303     validateValue : function(value)
12304     {
12305         if(this.getVisibilityEl().hasClass('hidden')){
12306             return true;
12307         }
12308         
12309         if(value.length < 1)  { // if it's blank
12310             if(this.allowBlank){
12311                 return true;
12312             }
12313             return false;
12314         }
12315         
12316         if(value.length < this.minLength){
12317             return false;
12318         }
12319         if(value.length > this.maxLength){
12320             return false;
12321         }
12322         if(this.vtype){
12323             var vt = Roo.form.VTypes;
12324             if(!vt[this.vtype](value, this)){
12325                 return false;
12326             }
12327         }
12328         if(typeof this.validator == "function"){
12329             var msg = this.validator(value);
12330             if(msg !== true){
12331                 return false;
12332             }
12333             if (typeof(msg) == 'string') {
12334                 this.invalidText = msg;
12335             }
12336         }
12337         
12338         if(this.regex && !this.regex.test(value)){
12339             return false;
12340         }
12341         
12342         return true;
12343     },
12344     
12345      // private
12346     fireKey : function(e){
12347         //Roo.log('field ' + e.getKey());
12348         if(e.isNavKeyPress()){
12349             this.fireEvent("specialkey", this, e);
12350         }
12351     },
12352     focus : function (selectText){
12353         if(this.rendered){
12354             this.inputEl().focus();
12355             if(selectText === true){
12356                 this.inputEl().dom.select();
12357             }
12358         }
12359         return this;
12360     } ,
12361     
12362     onFocus : function(){
12363         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12364            // this.el.addClass(this.focusClass);
12365         }
12366         if(!this.hasFocus){
12367             this.hasFocus = true;
12368             this.startValue = this.getValue();
12369             this.fireEvent("focus", this);
12370         }
12371     },
12372     
12373     beforeBlur : Roo.emptyFn,
12374
12375     
12376     // private
12377     onBlur : function(){
12378         this.beforeBlur();
12379         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12380             //this.el.removeClass(this.focusClass);
12381         }
12382         this.hasFocus = false;
12383         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12384             this.validate();
12385         }
12386         var v = this.getValue();
12387         if(String(v) !== String(this.startValue)){
12388             this.fireEvent('change', this, v, this.startValue);
12389         }
12390         this.fireEvent("blur", this);
12391     },
12392     
12393     onChange : function(e)
12394     {
12395         var v = this.getValue();
12396         if(String(v) !== String(this.startValue)){
12397             this.fireEvent('change', this, v, this.startValue);
12398         }
12399         
12400     },
12401     
12402     /**
12403      * Resets the current field value to the originally loaded value and clears any validation messages
12404      */
12405     reset : function(){
12406         this.setValue(this.originalValue);
12407         this.validate();
12408     },
12409      /**
12410      * Returns the name of the field
12411      * @return {Mixed} name The name field
12412      */
12413     getName: function(){
12414         return this.name;
12415     },
12416      /**
12417      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12418      * @return {Mixed} value The field value
12419      */
12420     getValue : function(){
12421         
12422         var v = this.inputEl().getValue();
12423         
12424         return v;
12425     },
12426     /**
12427      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12428      * @return {Mixed} value The field value
12429      */
12430     getRawValue : function(){
12431         var v = this.inputEl().getValue();
12432         
12433         return v;
12434     },
12435     
12436     /**
12437      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12438      * @param {Mixed} value The value to set
12439      */
12440     setRawValue : function(v){
12441         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12442     },
12443     
12444     selectText : function(start, end){
12445         var v = this.getRawValue();
12446         if(v.length > 0){
12447             start = start === undefined ? 0 : start;
12448             end = end === undefined ? v.length : end;
12449             var d = this.inputEl().dom;
12450             if(d.setSelectionRange){
12451                 d.setSelectionRange(start, end);
12452             }else if(d.createTextRange){
12453                 var range = d.createTextRange();
12454                 range.moveStart("character", start);
12455                 range.moveEnd("character", v.length-end);
12456                 range.select();
12457             }
12458         }
12459     },
12460     
12461     /**
12462      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12463      * @param {Mixed} value The value to set
12464      */
12465     setValue : function(v){
12466         this.value = v;
12467         if(this.rendered){
12468             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12469             this.validate();
12470         }
12471     },
12472     
12473     /*
12474     processValue : function(value){
12475         if(this.stripCharsRe){
12476             var newValue = value.replace(this.stripCharsRe, '');
12477             if(newValue !== value){
12478                 this.setRawValue(newValue);
12479                 return newValue;
12480             }
12481         }
12482         return value;
12483     },
12484   */
12485     preFocus : function(){
12486         
12487         if(this.selectOnFocus){
12488             this.inputEl().dom.select();
12489         }
12490     },
12491     filterKeys : function(e){
12492         var k = e.getKey();
12493         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12494             return;
12495         }
12496         var c = e.getCharCode(), cc = String.fromCharCode(c);
12497         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12498             return;
12499         }
12500         if(!this.maskRe.test(cc)){
12501             e.stopEvent();
12502         }
12503     },
12504      /**
12505      * Clear any invalid styles/messages for this field
12506      */
12507     clearInvalid : function(){
12508         
12509         if(!this.el || this.preventMark){ // not rendered
12510             return;
12511         }
12512         
12513         
12514         this.el.removeClass([this.invalidClass, 'is-invalid']);
12515         
12516         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12517             
12518             var feedback = this.el.select('.form-control-feedback', true).first();
12519             
12520             if(feedback){
12521                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12522             }
12523             
12524         }
12525         
12526         if(this.indicator){
12527             this.indicator.removeClass('visible');
12528             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12529         }
12530         
12531         this.fireEvent('valid', this);
12532     },
12533     
12534      /**
12535      * Mark this field as valid
12536      */
12537     markValid : function()
12538     {
12539         if(!this.el  || this.preventMark){ // not rendered...
12540             return;
12541         }
12542         
12543         this.el.removeClass([this.invalidClass, this.validClass]);
12544         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12545
12546         var feedback = this.el.select('.form-control-feedback', true).first();
12547             
12548         if(feedback){
12549             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12550         }
12551         
12552         if(this.indicator){
12553             this.indicator.removeClass('visible');
12554             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12555         }
12556         
12557         if(this.disabled){
12558             return;
12559         }
12560         
12561            
12562         if(this.allowBlank && !this.getRawValue().length){
12563             return;
12564         }
12565         if (Roo.bootstrap.version == 3) {
12566             this.el.addClass(this.validClass);
12567         } else {
12568             this.inputEl().addClass('is-valid');
12569         }
12570
12571         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12572             
12573             var feedback = this.el.select('.form-control-feedback', true).first();
12574             
12575             if(feedback){
12576                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12577                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12578             }
12579             
12580         }
12581         
12582         this.fireEvent('valid', this);
12583     },
12584     
12585      /**
12586      * Mark this field as invalid
12587      * @param {String} msg The validation message
12588      */
12589     markInvalid : function(msg)
12590     {
12591         if(!this.el  || this.preventMark){ // not rendered
12592             return;
12593         }
12594         
12595         this.el.removeClass([this.invalidClass, this.validClass]);
12596         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12597         
12598         var feedback = this.el.select('.form-control-feedback', true).first();
12599             
12600         if(feedback){
12601             this.el.select('.form-control-feedback', true).first().removeClass(
12602                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12603         }
12604
12605         if(this.disabled){
12606             return;
12607         }
12608         
12609         if(this.allowBlank && !this.getRawValue().length){
12610             return;
12611         }
12612         
12613         if(this.indicator){
12614             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12615             this.indicator.addClass('visible');
12616         }
12617         if (Roo.bootstrap.version == 3) {
12618             this.el.addClass(this.invalidClass);
12619         } else {
12620             this.inputEl().addClass('is-invalid');
12621         }
12622         
12623         
12624         
12625         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12626             
12627             var feedback = this.el.select('.form-control-feedback', true).first();
12628             
12629             if(feedback){
12630                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12631                 
12632                 if(this.getValue().length || this.forceFeedback){
12633                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12634                 }
12635                 
12636             }
12637             
12638         }
12639         
12640         this.fireEvent('invalid', this, msg);
12641     },
12642     // private
12643     SafariOnKeyDown : function(event)
12644     {
12645         // this is a workaround for a password hang bug on chrome/ webkit.
12646         if (this.inputEl().dom.type != 'password') {
12647             return;
12648         }
12649         
12650         var isSelectAll = false;
12651         
12652         if(this.inputEl().dom.selectionEnd > 0){
12653             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12654         }
12655         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12656             event.preventDefault();
12657             this.setValue('');
12658             return;
12659         }
12660         
12661         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12662             
12663             event.preventDefault();
12664             // this is very hacky as keydown always get's upper case.
12665             //
12666             var cc = String.fromCharCode(event.getCharCode());
12667             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12668             
12669         }
12670     },
12671     adjustWidth : function(tag, w){
12672         tag = tag.toLowerCase();
12673         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12674             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12675                 if(tag == 'input'){
12676                     return w + 2;
12677                 }
12678                 if(tag == 'textarea'){
12679                     return w-2;
12680                 }
12681             }else if(Roo.isOpera){
12682                 if(tag == 'input'){
12683                     return w + 2;
12684                 }
12685                 if(tag == 'textarea'){
12686                     return w-2;
12687                 }
12688             }
12689         }
12690         return w;
12691     },
12692     
12693     setFieldLabel : function(v)
12694     {
12695         if(!this.rendered){
12696             return;
12697         }
12698         
12699         if(this.indicatorEl()){
12700             var ar = this.el.select('label > span',true);
12701             
12702             if (ar.elements.length) {
12703                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12704                 this.fieldLabel = v;
12705                 return;
12706             }
12707             
12708             var br = this.el.select('label',true);
12709             
12710             if(br.elements.length) {
12711                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12712                 this.fieldLabel = v;
12713                 return;
12714             }
12715             
12716             Roo.log('Cannot Found any of label > span || label in input');
12717             return;
12718         }
12719         
12720         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12721         this.fieldLabel = v;
12722         
12723         
12724     }
12725 });
12726
12727  
12728 /*
12729  * - LGPL
12730  *
12731  * Input
12732  * 
12733  */
12734
12735 /**
12736  * @class Roo.bootstrap.TextArea
12737  * @extends Roo.bootstrap.Input
12738  * Bootstrap TextArea class
12739  * @cfg {Number} cols Specifies the visible width of a text area
12740  * @cfg {Number} rows Specifies the visible number of lines in a text area
12741  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12742  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12743  * @cfg {string} html text
12744  * 
12745  * @constructor
12746  * Create a new TextArea
12747  * @param {Object} config The config object
12748  */
12749
12750 Roo.bootstrap.TextArea = function(config){
12751     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12752    
12753 };
12754
12755 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12756      
12757     cols : false,
12758     rows : 5,
12759     readOnly : false,
12760     warp : 'soft',
12761     resize : false,
12762     value: false,
12763     html: false,
12764     
12765     getAutoCreate : function(){
12766         
12767         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12768         
12769         var id = Roo.id();
12770         
12771         var cfg = {};
12772         
12773         if(this.inputType != 'hidden'){
12774             cfg.cls = 'form-group' //input-group
12775         }
12776         
12777         var input =  {
12778             tag: 'textarea',
12779             id : id,
12780             warp : this.warp,
12781             rows : this.rows,
12782             value : this.value || '',
12783             html: this.html || '',
12784             cls : 'form-control',
12785             placeholder : this.placeholder || '' 
12786             
12787         };
12788         
12789         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12790             input.maxLength = this.maxLength;
12791         }
12792         
12793         if(this.resize){
12794             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12795         }
12796         
12797         if(this.cols){
12798             input.cols = this.cols;
12799         }
12800         
12801         if (this.readOnly) {
12802             input.readonly = true;
12803         }
12804         
12805         if (this.name) {
12806             input.name = this.name;
12807         }
12808         
12809         if (this.size) {
12810             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12811         }
12812         
12813         var settings=this;
12814         ['xs','sm','md','lg'].map(function(size){
12815             if (settings[size]) {
12816                 cfg.cls += ' col-' + size + '-' + settings[size];
12817             }
12818         });
12819         
12820         var inputblock = input;
12821         
12822         if(this.hasFeedback && !this.allowBlank){
12823             
12824             var feedback = {
12825                 tag: 'span',
12826                 cls: 'glyphicon form-control-feedback'
12827             };
12828
12829             inputblock = {
12830                 cls : 'has-feedback',
12831                 cn :  [
12832                     input,
12833                     feedback
12834                 ] 
12835             };  
12836         }
12837         
12838         
12839         if (this.before || this.after) {
12840             
12841             inputblock = {
12842                 cls : 'input-group',
12843                 cn :  [] 
12844             };
12845             if (this.before) {
12846                 inputblock.cn.push({
12847                     tag :'span',
12848                     cls : 'input-group-addon',
12849                     html : this.before
12850                 });
12851             }
12852             
12853             inputblock.cn.push(input);
12854             
12855             if(this.hasFeedback && !this.allowBlank){
12856                 inputblock.cls += ' has-feedback';
12857                 inputblock.cn.push(feedback);
12858             }
12859             
12860             if (this.after) {
12861                 inputblock.cn.push({
12862                     tag :'span',
12863                     cls : 'input-group-addon',
12864                     html : this.after
12865                 });
12866             }
12867             
12868         }
12869         
12870         if (align ==='left' && this.fieldLabel.length) {
12871             cfg.cn = [
12872                 {
12873                     tag: 'label',
12874                     'for' :  id,
12875                     cls : 'control-label',
12876                     html : this.fieldLabel
12877                 },
12878                 {
12879                     cls : "",
12880                     cn: [
12881                         inputblock
12882                     ]
12883                 }
12884
12885             ];
12886             
12887             if(this.labelWidth > 12){
12888                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12889             }
12890
12891             if(this.labelWidth < 13 && this.labelmd == 0){
12892                 this.labelmd = this.labelWidth;
12893             }
12894
12895             if(this.labellg > 0){
12896                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12897                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12898             }
12899
12900             if(this.labelmd > 0){
12901                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12902                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12903             }
12904
12905             if(this.labelsm > 0){
12906                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12907                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12908             }
12909
12910             if(this.labelxs > 0){
12911                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12912                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12913             }
12914             
12915         } else if ( this.fieldLabel.length) {
12916             cfg.cn = [
12917
12918                {
12919                    tag: 'label',
12920                    //cls : 'input-group-addon',
12921                    html : this.fieldLabel
12922
12923                },
12924
12925                inputblock
12926
12927            ];
12928
12929         } else {
12930
12931             cfg.cn = [
12932
12933                 inputblock
12934
12935             ];
12936                 
12937         }
12938         
12939         if (this.disabled) {
12940             input.disabled=true;
12941         }
12942         
12943         return cfg;
12944         
12945     },
12946     /**
12947      * return the real textarea element.
12948      */
12949     inputEl: function ()
12950     {
12951         return this.el.select('textarea.form-control',true).first();
12952     },
12953     
12954     /**
12955      * Clear any invalid styles/messages for this field
12956      */
12957     clearInvalid : function()
12958     {
12959         
12960         if(!this.el || this.preventMark){ // not rendered
12961             return;
12962         }
12963         
12964         var label = this.el.select('label', true).first();
12965         var icon = this.el.select('i.fa-star', true).first();
12966         
12967         if(label && icon){
12968             icon.remove();
12969         }
12970         this.el.removeClass( this.validClass);
12971         this.inputEl().removeClass('is-invalid');
12972          
12973         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12974             
12975             var feedback = this.el.select('.form-control-feedback', true).first();
12976             
12977             if(feedback){
12978                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12979             }
12980             
12981         }
12982         
12983         this.fireEvent('valid', this);
12984     },
12985     
12986      /**
12987      * Mark this field as valid
12988      */
12989     markValid : function()
12990     {
12991         if(!this.el  || this.preventMark){ // not rendered
12992             return;
12993         }
12994         
12995         this.el.removeClass([this.invalidClass, this.validClass]);
12996         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12997         
12998         var feedback = this.el.select('.form-control-feedback', true).first();
12999             
13000         if(feedback){
13001             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13002         }
13003
13004         if(this.disabled || this.allowBlank){
13005             return;
13006         }
13007         
13008         var label = this.el.select('label', true).first();
13009         var icon = this.el.select('i.fa-star', true).first();
13010         
13011         if(label && icon){
13012             icon.remove();
13013         }
13014         if (Roo.bootstrap.version == 3) {
13015             this.el.addClass(this.validClass);
13016         } else {
13017             this.inputEl().addClass('is-valid');
13018         }
13019         
13020         
13021         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13022             
13023             var feedback = this.el.select('.form-control-feedback', true).first();
13024             
13025             if(feedback){
13026                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13027                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13028             }
13029             
13030         }
13031         
13032         this.fireEvent('valid', this);
13033     },
13034     
13035      /**
13036      * Mark this field as invalid
13037      * @param {String} msg The validation message
13038      */
13039     markInvalid : function(msg)
13040     {
13041         if(!this.el  || this.preventMark){ // not rendered
13042             return;
13043         }
13044         
13045         this.el.removeClass([this.invalidClass, this.validClass]);
13046         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13047         
13048         var feedback = this.el.select('.form-control-feedback', true).first();
13049             
13050         if(feedback){
13051             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13052         }
13053
13054         if(this.disabled || this.allowBlank){
13055             return;
13056         }
13057         
13058         var label = this.el.select('label', true).first();
13059         var icon = this.el.select('i.fa-star', true).first();
13060         
13061         if(!this.getValue().length && label && !icon){
13062             this.el.createChild({
13063                 tag : 'i',
13064                 cls : 'text-danger fa fa-lg fa-star',
13065                 tooltip : 'This field is required',
13066                 style : 'margin-right:5px;'
13067             }, label, true);
13068         }
13069         
13070         if (Roo.bootstrap.version == 3) {
13071             this.el.addClass(this.invalidClass);
13072         } else {
13073             this.inputEl().addClass('is-invalid');
13074         }
13075         
13076         // fixme ... this may be depricated need to test..
13077         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13078             
13079             var feedback = this.el.select('.form-control-feedback', true).first();
13080             
13081             if(feedback){
13082                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13083                 
13084                 if(this.getValue().length || this.forceFeedback){
13085                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13086                 }
13087                 
13088             }
13089             
13090         }
13091         
13092         this.fireEvent('invalid', this, msg);
13093     }
13094 });
13095
13096  
13097 /*
13098  * - LGPL
13099  *
13100  * trigger field - base class for combo..
13101  * 
13102  */
13103  
13104 /**
13105  * @class Roo.bootstrap.TriggerField
13106  * @extends Roo.bootstrap.Input
13107  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13108  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13109  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13110  * for which you can provide a custom implementation.  For example:
13111  * <pre><code>
13112 var trigger = new Roo.bootstrap.TriggerField();
13113 trigger.onTriggerClick = myTriggerFn;
13114 trigger.applyTo('my-field');
13115 </code></pre>
13116  *
13117  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13118  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13119  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13120  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13121  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13122
13123  * @constructor
13124  * Create a new TriggerField.
13125  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13126  * to the base TextField)
13127  */
13128 Roo.bootstrap.TriggerField = function(config){
13129     this.mimicing = false;
13130     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13131 };
13132
13133 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13134     /**
13135      * @cfg {String} triggerClass A CSS class to apply to the trigger
13136      */
13137      /**
13138      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13139      */
13140     hideTrigger:false,
13141
13142     /**
13143      * @cfg {Boolean} removable (true|false) special filter default false
13144      */
13145     removable : false,
13146     
13147     /** @cfg {Boolean} grow @hide */
13148     /** @cfg {Number} growMin @hide */
13149     /** @cfg {Number} growMax @hide */
13150
13151     /**
13152      * @hide 
13153      * @method
13154      */
13155     autoSize: Roo.emptyFn,
13156     // private
13157     monitorTab : true,
13158     // private
13159     deferHeight : true,
13160
13161     
13162     actionMode : 'wrap',
13163     
13164     caret : false,
13165     
13166     
13167     getAutoCreate : function(){
13168        
13169         var align = this.labelAlign || this.parentLabelAlign();
13170         
13171         var id = Roo.id();
13172         
13173         var cfg = {
13174             cls: 'form-group' //input-group
13175         };
13176         
13177         
13178         var input =  {
13179             tag: 'input',
13180             id : id,
13181             type : this.inputType,
13182             cls : 'form-control',
13183             autocomplete: 'new-password',
13184             placeholder : this.placeholder || '' 
13185             
13186         };
13187         if (this.name) {
13188             input.name = this.name;
13189         }
13190         if (this.size) {
13191             input.cls += ' input-' + this.size;
13192         }
13193         
13194         if (this.disabled) {
13195             input.disabled=true;
13196         }
13197         
13198         var inputblock = input;
13199         
13200         if(this.hasFeedback && !this.allowBlank){
13201             
13202             var feedback = {
13203                 tag: 'span',
13204                 cls: 'glyphicon form-control-feedback'
13205             };
13206             
13207             if(this.removable && !this.editable  ){
13208                 inputblock = {
13209                     cls : 'has-feedback',
13210                     cn :  [
13211                         inputblock,
13212                         {
13213                             tag: 'button',
13214                             html : 'x',
13215                             cls : 'roo-combo-removable-btn close'
13216                         },
13217                         feedback
13218                     ] 
13219                 };
13220             } else {
13221                 inputblock = {
13222                     cls : 'has-feedback',
13223                     cn :  [
13224                         inputblock,
13225                         feedback
13226                     ] 
13227                 };
13228             }
13229
13230         } else {
13231             if(this.removable && !this.editable ){
13232                 inputblock = {
13233                     cls : 'roo-removable',
13234                     cn :  [
13235                         inputblock,
13236                         {
13237                             tag: 'button',
13238                             html : 'x',
13239                             cls : 'roo-combo-removable-btn close'
13240                         }
13241                     ] 
13242                 };
13243             }
13244         }
13245         
13246         if (this.before || this.after) {
13247             
13248             inputblock = {
13249                 cls : 'input-group',
13250                 cn :  [] 
13251             };
13252             if (this.before) {
13253                 inputblock.cn.push({
13254                     tag :'span',
13255                     cls : 'input-group-addon input-group-prepend input-group-text',
13256                     html : this.before
13257                 });
13258             }
13259             
13260             inputblock.cn.push(input);
13261             
13262             if(this.hasFeedback && !this.allowBlank){
13263                 inputblock.cls += ' has-feedback';
13264                 inputblock.cn.push(feedback);
13265             }
13266             
13267             if (this.after) {
13268                 inputblock.cn.push({
13269                     tag :'span',
13270                     cls : 'input-group-addon input-group-append input-group-text',
13271                     html : this.after
13272                 });
13273             }
13274             
13275         };
13276         
13277       
13278         
13279         var ibwrap = inputblock;
13280         
13281         if(this.multiple){
13282             ibwrap = {
13283                 tag: 'ul',
13284                 cls: 'roo-select2-choices',
13285                 cn:[
13286                     {
13287                         tag: 'li',
13288                         cls: 'roo-select2-search-field',
13289                         cn: [
13290
13291                             inputblock
13292                         ]
13293                     }
13294                 ]
13295             };
13296                 
13297         }
13298         
13299         var combobox = {
13300             cls: 'roo-select2-container input-group',
13301             cn: [
13302                  {
13303                     tag: 'input',
13304                     type : 'hidden',
13305                     cls: 'form-hidden-field'
13306                 },
13307                 ibwrap
13308             ]
13309         };
13310         
13311         if(!this.multiple && this.showToggleBtn){
13312             
13313             var caret = {
13314                         tag: 'span',
13315                         cls: 'caret'
13316              };
13317             if (this.caret != false) {
13318                 caret = {
13319                      tag: 'i',
13320                      cls: 'fa fa-' + this.caret
13321                 };
13322                 
13323             }
13324             
13325             combobox.cn.push({
13326                 tag :'span',
13327                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13328                 cn : [
13329                     Roo.bootstrap.version == 3 ? caret : '',
13330                     {
13331                         tag: 'span',
13332                         cls: 'combobox-clear',
13333                         cn  : [
13334                             {
13335                                 tag : 'i',
13336                                 cls: 'icon-remove'
13337                             }
13338                         ]
13339                     }
13340                 ]
13341
13342             })
13343         }
13344         
13345         if(this.multiple){
13346             combobox.cls += ' roo-select2-container-multi';
13347         }
13348          var indicator = {
13349             tag : 'i',
13350             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13351             tooltip : 'This field is required'
13352         };
13353         if (Roo.bootstrap.version == 4) {
13354             indicator = {
13355                 tag : 'i',
13356                 style : 'display:none'
13357             };
13358         }
13359         
13360         
13361         if (align ==='left' && this.fieldLabel.length) {
13362             
13363             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13364
13365             cfg.cn = [
13366                 indicator,
13367                 {
13368                     tag: 'label',
13369                     'for' :  id,
13370                     cls : 'control-label',
13371                     html : this.fieldLabel
13372
13373                 },
13374                 {
13375                     cls : "", 
13376                     cn: [
13377                         combobox
13378                     ]
13379                 }
13380
13381             ];
13382             
13383             var labelCfg = cfg.cn[1];
13384             var contentCfg = cfg.cn[2];
13385             
13386             if(this.indicatorpos == 'right'){
13387                 cfg.cn = [
13388                     {
13389                         tag: 'label',
13390                         'for' :  id,
13391                         cls : 'control-label',
13392                         cn : [
13393                             {
13394                                 tag : 'span',
13395                                 html : this.fieldLabel
13396                             },
13397                             indicator
13398                         ]
13399                     },
13400                     {
13401                         cls : "", 
13402                         cn: [
13403                             combobox
13404                         ]
13405                     }
13406
13407                 ];
13408                 
13409                 labelCfg = cfg.cn[0];
13410                 contentCfg = cfg.cn[1];
13411             }
13412             
13413             if(this.labelWidth > 12){
13414                 labelCfg.style = "width: " + this.labelWidth + 'px';
13415             }
13416             
13417             if(this.labelWidth < 13 && this.labelmd == 0){
13418                 this.labelmd = this.labelWidth;
13419             }
13420             
13421             if(this.labellg > 0){
13422                 labelCfg.cls += ' col-lg-' + this.labellg;
13423                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13424             }
13425             
13426             if(this.labelmd > 0){
13427                 labelCfg.cls += ' col-md-' + this.labelmd;
13428                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13429             }
13430             
13431             if(this.labelsm > 0){
13432                 labelCfg.cls += ' col-sm-' + this.labelsm;
13433                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13434             }
13435             
13436             if(this.labelxs > 0){
13437                 labelCfg.cls += ' col-xs-' + this.labelxs;
13438                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13439             }
13440             
13441         } else if ( this.fieldLabel.length) {
13442 //                Roo.log(" label");
13443             cfg.cn = [
13444                 indicator,
13445                {
13446                    tag: 'label',
13447                    //cls : 'input-group-addon',
13448                    html : this.fieldLabel
13449
13450                },
13451
13452                combobox
13453
13454             ];
13455             
13456             if(this.indicatorpos == 'right'){
13457                 
13458                 cfg.cn = [
13459                     {
13460                        tag: 'label',
13461                        cn : [
13462                            {
13463                                tag : 'span',
13464                                html : this.fieldLabel
13465                            },
13466                            indicator
13467                        ]
13468
13469                     },
13470                     combobox
13471
13472                 ];
13473
13474             }
13475
13476         } else {
13477             
13478 //                Roo.log(" no label && no align");
13479                 cfg = combobox
13480                      
13481                 
13482         }
13483         
13484         var settings=this;
13485         ['xs','sm','md','lg'].map(function(size){
13486             if (settings[size]) {
13487                 cfg.cls += ' col-' + size + '-' + settings[size];
13488             }
13489         });
13490         
13491         return cfg;
13492         
13493     },
13494     
13495     
13496     
13497     // private
13498     onResize : function(w, h){
13499 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13500 //        if(typeof w == 'number'){
13501 //            var x = w - this.trigger.getWidth();
13502 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13503 //            this.trigger.setStyle('left', x+'px');
13504 //        }
13505     },
13506
13507     // private
13508     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13509
13510     // private
13511     getResizeEl : function(){
13512         return this.inputEl();
13513     },
13514
13515     // private
13516     getPositionEl : function(){
13517         return this.inputEl();
13518     },
13519
13520     // private
13521     alignErrorIcon : function(){
13522         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13523     },
13524
13525     // private
13526     initEvents : function(){
13527         
13528         this.createList();
13529         
13530         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13531         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13532         if(!this.multiple && this.showToggleBtn){
13533             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13534             if(this.hideTrigger){
13535                 this.trigger.setDisplayed(false);
13536             }
13537             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13538         }
13539         
13540         if(this.multiple){
13541             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13542         }
13543         
13544         if(this.removable && !this.editable && !this.tickable){
13545             var close = this.closeTriggerEl();
13546             
13547             if(close){
13548                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13549                 close.on('click', this.removeBtnClick, this, close);
13550             }
13551         }
13552         
13553         //this.trigger.addClassOnOver('x-form-trigger-over');
13554         //this.trigger.addClassOnClick('x-form-trigger-click');
13555         
13556         //if(!this.width){
13557         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13558         //}
13559     },
13560     
13561     closeTriggerEl : function()
13562     {
13563         var close = this.el.select('.roo-combo-removable-btn', true).first();
13564         return close ? close : false;
13565     },
13566     
13567     removeBtnClick : function(e, h, el)
13568     {
13569         e.preventDefault();
13570         
13571         if(this.fireEvent("remove", this) !== false){
13572             this.reset();
13573             this.fireEvent("afterremove", this)
13574         }
13575     },
13576     
13577     createList : function()
13578     {
13579         this.list = Roo.get(document.body).createChild({
13580             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13581             cls: 'typeahead typeahead-long dropdown-menu shadow',
13582             style: 'display:none'
13583         });
13584         
13585         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13586         
13587     },
13588
13589     // private
13590     initTrigger : function(){
13591        
13592     },
13593
13594     // private
13595     onDestroy : function(){
13596         if(this.trigger){
13597             this.trigger.removeAllListeners();
13598           //  this.trigger.remove();
13599         }
13600         //if(this.wrap){
13601         //    this.wrap.remove();
13602         //}
13603         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13604     },
13605
13606     // private
13607     onFocus : function(){
13608         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13609         /*
13610         if(!this.mimicing){
13611             this.wrap.addClass('x-trigger-wrap-focus');
13612             this.mimicing = true;
13613             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13614             if(this.monitorTab){
13615                 this.el.on("keydown", this.checkTab, this);
13616             }
13617         }
13618         */
13619     },
13620
13621     // private
13622     checkTab : function(e){
13623         if(e.getKey() == e.TAB){
13624             this.triggerBlur();
13625         }
13626     },
13627
13628     // private
13629     onBlur : function(){
13630         // do nothing
13631     },
13632
13633     // private
13634     mimicBlur : function(e, t){
13635         /*
13636         if(!this.wrap.contains(t) && this.validateBlur()){
13637             this.triggerBlur();
13638         }
13639         */
13640     },
13641
13642     // private
13643     triggerBlur : function(){
13644         this.mimicing = false;
13645         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13646         if(this.monitorTab){
13647             this.el.un("keydown", this.checkTab, this);
13648         }
13649         //this.wrap.removeClass('x-trigger-wrap-focus');
13650         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13651     },
13652
13653     // private
13654     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13655     validateBlur : function(e, t){
13656         return true;
13657     },
13658
13659     // private
13660     onDisable : function(){
13661         this.inputEl().dom.disabled = true;
13662         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13663         //if(this.wrap){
13664         //    this.wrap.addClass('x-item-disabled');
13665         //}
13666     },
13667
13668     // private
13669     onEnable : function(){
13670         this.inputEl().dom.disabled = false;
13671         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13672         //if(this.wrap){
13673         //    this.el.removeClass('x-item-disabled');
13674         //}
13675     },
13676
13677     // private
13678     onShow : function(){
13679         var ae = this.getActionEl();
13680         
13681         if(ae){
13682             ae.dom.style.display = '';
13683             ae.dom.style.visibility = 'visible';
13684         }
13685     },
13686
13687     // private
13688     
13689     onHide : function(){
13690         var ae = this.getActionEl();
13691         ae.dom.style.display = 'none';
13692     },
13693
13694     /**
13695      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13696      * by an implementing function.
13697      * @method
13698      * @param {EventObject} e
13699      */
13700     onTriggerClick : Roo.emptyFn
13701 });
13702  
13703 /*
13704 * Licence: LGPL
13705 */
13706
13707 /**
13708  * @class Roo.bootstrap.CardUploader
13709  * @extends Roo.bootstrap.Button
13710  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13711  * @cfg {Number} errorTimeout default 3000
13712  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13713  * @cfg {Array}  html The button text.
13714
13715  *
13716  * @constructor
13717  * Create a new CardUploader
13718  * @param {Object} config The config object
13719  */
13720
13721 Roo.bootstrap.CardUploader = function(config){
13722     
13723  
13724     
13725     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13726     
13727     
13728     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13729         return r.data.id
13730      });
13731     
13732      this.addEvents({
13733          // raw events
13734         /**
13735          * @event preview
13736          * When a image is clicked on - and needs to display a slideshow or similar..
13737          * @param {Roo.bootstrap.Card} this
13738          * @param {Object} The image information data 
13739          *
13740          */
13741         'preview' : true,
13742          /**
13743          * @event download
13744          * When a the download link is clicked
13745          * @param {Roo.bootstrap.Card} this
13746          * @param {Object} The image information data  contains 
13747          */
13748         'download' : true
13749         
13750     });
13751 };
13752  
13753 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13754     
13755      
13756     errorTimeout : 3000,
13757      
13758     images : false,
13759    
13760     fileCollection : false,
13761     allowBlank : true,
13762     
13763     getAutoCreate : function()
13764     {
13765         
13766         var cfg =  {
13767             cls :'form-group' ,
13768             cn : [
13769                
13770                 {
13771                     tag: 'label',
13772                    //cls : 'input-group-addon',
13773                     html : this.fieldLabel
13774
13775                 },
13776
13777                 {
13778                     tag: 'input',
13779                     type : 'hidden',
13780                     name : this.name,
13781                     value : this.value,
13782                     cls : 'd-none  form-control'
13783                 },
13784                 
13785                 {
13786                     tag: 'input',
13787                     multiple : 'multiple',
13788                     type : 'file',
13789                     cls : 'd-none  roo-card-upload-selector'
13790                 },
13791                 
13792                 {
13793                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13794                 },
13795                 {
13796                     cls : 'card-columns roo-card-uploader-container'
13797                 }
13798
13799             ]
13800         };
13801            
13802          
13803         return cfg;
13804     },
13805     
13806     getChildContainer : function() /// what children are added to.
13807     {
13808         return this.containerEl;
13809     },
13810    
13811     getButtonContainer : function() /// what children are added to.
13812     {
13813         return this.el.select(".roo-card-uploader-button-container").first();
13814     },
13815    
13816     initEvents : function()
13817     {
13818         
13819         Roo.bootstrap.Input.prototype.initEvents.call(this);
13820         
13821         var t = this;
13822         this.addxtype({
13823             xns: Roo.bootstrap,
13824
13825             xtype : 'Button',
13826             container_method : 'getButtonContainer' ,            
13827             html :  this.html, // fix changable?
13828             cls : 'w-100 ',
13829             listeners : {
13830                 'click' : function(btn, e) {
13831                     t.onClick(e);
13832                 }
13833             }
13834         });
13835         
13836         
13837         
13838         
13839         this.urlAPI = (window.createObjectURL && window) || 
13840                                 (window.URL && URL.revokeObjectURL && URL) || 
13841                                 (window.webkitURL && webkitURL);
13842                         
13843          
13844          
13845          
13846         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13847         
13848         this.selectorEl.on('change', this.onFileSelected, this);
13849         if (this.images) {
13850             var t = this;
13851             this.images.forEach(function(img) {
13852                 t.addCard(img)
13853             });
13854             this.images = false;
13855         }
13856         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13857          
13858        
13859     },
13860     
13861    
13862     onClick : function(e)
13863     {
13864         e.preventDefault();
13865          
13866         this.selectorEl.dom.click();
13867          
13868     },
13869     
13870     onFileSelected : function(e)
13871     {
13872         e.preventDefault();
13873         
13874         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13875             return;
13876         }
13877         
13878         Roo.each(this.selectorEl.dom.files, function(file){    
13879             this.addFile(file);
13880         }, this);
13881          
13882     },
13883     
13884       
13885     
13886       
13887     
13888     addFile : function(file)
13889     {
13890            
13891         if(typeof(file) === 'string'){
13892             throw "Add file by name?"; // should not happen
13893             return;
13894         }
13895         
13896         if(!file || !this.urlAPI){
13897             return;
13898         }
13899         
13900         // file;
13901         // file.type;
13902         
13903         var _this = this;
13904         
13905         
13906         var url = _this.urlAPI.createObjectURL( file);
13907            
13908         this.addCard({
13909             id : Roo.bootstrap.CardUploader.ID--,
13910             is_uploaded : false,
13911             src : url,
13912             srcfile : file,
13913             title : file.name,
13914             mimetype : file.type,
13915             preview : false,
13916             is_deleted : 0
13917         });
13918         
13919     },
13920     
13921     /**
13922      * addCard - add an Attachment to the uploader
13923      * @param data - the data about the image to upload
13924      *
13925      * {
13926           id : 123
13927           title : "Title of file",
13928           is_uploaded : false,
13929           src : "http://.....",
13930           srcfile : { the File upload object },
13931           mimetype : file.type,
13932           preview : false,
13933           is_deleted : 0
13934           .. any other data...
13935         }
13936      *
13937      * 
13938     */
13939     
13940     addCard : function (data)
13941     {
13942         // hidden input element?
13943         // if the file is not an image...
13944         //then we need to use something other that and header_image
13945         var t = this;
13946         //   remove.....
13947         var footer = [
13948             {
13949                 xns : Roo.bootstrap,
13950                 xtype : 'CardFooter',
13951                  items: [
13952                     {
13953                         xns : Roo.bootstrap,
13954                         xtype : 'Element',
13955                         cls : 'd-flex',
13956                         items : [
13957                             
13958                             {
13959                                 xns : Roo.bootstrap,
13960                                 xtype : 'Button',
13961                                 html : String.format("<small>{0}</small>", data.title),
13962                                 cls : 'col-10 text-left',
13963                                 size: 'sm',
13964                                 weight: 'link',
13965                                 fa : 'download',
13966                                 listeners : {
13967                                     click : function() {
13968                                      
13969                                         t.fireEvent( "download", t, data );
13970                                     }
13971                                 }
13972                             },
13973                           
13974                             {
13975                                 xns : Roo.bootstrap,
13976                                 xtype : 'Button',
13977                                 style: 'max-height: 28px; ',
13978                                 size : 'sm',
13979                                 weight: 'danger',
13980                                 cls : 'col-2',
13981                                 fa : 'times',
13982                                 listeners : {
13983                                     click : function() {
13984                                         t.removeCard(data.id)
13985                                     }
13986                                 }
13987                             }
13988                         ]
13989                     }
13990                     
13991                 ] 
13992             }
13993             
13994         ];
13995         
13996         var cn = this.addxtype(
13997             {
13998                  
13999                 xns : Roo.bootstrap,
14000                 xtype : 'Card',
14001                 closeable : true,
14002                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14003                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14004                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14005                 data : data,
14006                 html : false,
14007                  
14008                 items : footer,
14009                 initEvents : function() {
14010                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14011                     var card = this;
14012                     this.imgEl = this.el.select('.card-img-top').first();
14013                     if (this.imgEl) {
14014                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14015                         this.imgEl.set({ 'pointer' : 'cursor' });
14016                                   
14017                     }
14018                     this.getCardFooter().addClass('p-1');
14019                     
14020                   
14021                 }
14022                 
14023             }
14024         );
14025         // dont' really need ot update items.
14026         // this.items.push(cn);
14027         this.fileCollection.add(cn);
14028         
14029         if (!data.srcfile) {
14030             this.updateInput();
14031             return;
14032         }
14033             
14034         var _t = this;
14035         var reader = new FileReader();
14036         reader.addEventListener("load", function() {  
14037             data.srcdata =  reader.result;
14038             _t.updateInput();
14039         });
14040         reader.readAsDataURL(data.srcfile);
14041         
14042         
14043         
14044     },
14045     removeCard : function(id)
14046     {
14047         
14048         var card  = this.fileCollection.get(id);
14049         card.data.is_deleted = 1;
14050         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14051         //this.fileCollection.remove(card);
14052         //this.items = this.items.filter(function(e) { return e != card });
14053         // dont' really need ot update items.
14054         card.el.dom.parentNode.removeChild(card.el.dom);
14055         this.updateInput();
14056
14057         
14058     },
14059     reset: function()
14060     {
14061         this.fileCollection.each(function(card) {
14062             if (card.el.dom && card.el.dom.parentNode) {
14063                 card.el.dom.parentNode.removeChild(card.el.dom);
14064             }
14065         });
14066         this.fileCollection.clear();
14067         this.updateInput();
14068     },
14069     
14070     updateInput : function()
14071     {
14072          var data = [];
14073         this.fileCollection.each(function(e) {
14074             data.push(e.data);
14075             
14076         });
14077         this.inputEl().dom.value = JSON.stringify(data);
14078         
14079         
14080         
14081     }
14082     
14083     
14084 });
14085
14086
14087 Roo.bootstrap.CardUploader.ID = -1;/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097
14098
14099 /**
14100  * @class Roo.data.SortTypes
14101  * @singleton
14102  * Defines the default sorting (casting?) comparison functions used when sorting data.
14103  */
14104 Roo.data.SortTypes = {
14105     /**
14106      * Default sort that does nothing
14107      * @param {Mixed} s The value being converted
14108      * @return {Mixed} The comparison value
14109      */
14110     none : function(s){
14111         return s;
14112     },
14113     
14114     /**
14115      * The regular expression used to strip tags
14116      * @type {RegExp}
14117      * @property
14118      */
14119     stripTagsRE : /<\/?[^>]+>/gi,
14120     
14121     /**
14122      * Strips all HTML tags to sort on text only
14123      * @param {Mixed} s The value being converted
14124      * @return {String} The comparison value
14125      */
14126     asText : function(s){
14127         return String(s).replace(this.stripTagsRE, "");
14128     },
14129     
14130     /**
14131      * Strips all HTML tags to sort on text only - Case insensitive
14132      * @param {Mixed} s The value being converted
14133      * @return {String} The comparison value
14134      */
14135     asUCText : function(s){
14136         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14137     },
14138     
14139     /**
14140      * Case insensitive string
14141      * @param {Mixed} s The value being converted
14142      * @return {String} The comparison value
14143      */
14144     asUCString : function(s) {
14145         return String(s).toUpperCase();
14146     },
14147     
14148     /**
14149      * Date sorting
14150      * @param {Mixed} s The value being converted
14151      * @return {Number} The comparison value
14152      */
14153     asDate : function(s) {
14154         if(!s){
14155             return 0;
14156         }
14157         if(s instanceof Date){
14158             return s.getTime();
14159         }
14160         return Date.parse(String(s));
14161     },
14162     
14163     /**
14164      * Float sorting
14165      * @param {Mixed} s The value being converted
14166      * @return {Float} The comparison value
14167      */
14168     asFloat : function(s) {
14169         var val = parseFloat(String(s).replace(/,/g, ""));
14170         if(isNaN(val)) {
14171             val = 0;
14172         }
14173         return val;
14174     },
14175     
14176     /**
14177      * Integer sorting
14178      * @param {Mixed} s The value being converted
14179      * @return {Number} The comparison value
14180      */
14181     asInt : function(s) {
14182         var val = parseInt(String(s).replace(/,/g, ""));
14183         if(isNaN(val)) {
14184             val = 0;
14185         }
14186         return val;
14187     }
14188 };/*
14189  * Based on:
14190  * Ext JS Library 1.1.1
14191  * Copyright(c) 2006-2007, Ext JS, LLC.
14192  *
14193  * Originally Released Under LGPL - original licence link has changed is not relivant.
14194  *
14195  * Fork - LGPL
14196  * <script type="text/javascript">
14197  */
14198
14199 /**
14200 * @class Roo.data.Record
14201  * Instances of this class encapsulate both record <em>definition</em> information, and record
14202  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14203  * to access Records cached in an {@link Roo.data.Store} object.<br>
14204  * <p>
14205  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14206  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14207  * objects.<br>
14208  * <p>
14209  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14210  * @constructor
14211  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14212  * {@link #create}. The parameters are the same.
14213  * @param {Array} data An associative Array of data values keyed by the field name.
14214  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14215  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14216  * not specified an integer id is generated.
14217  */
14218 Roo.data.Record = function(data, id){
14219     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14220     this.data = data;
14221 };
14222
14223 /**
14224  * Generate a constructor for a specific record layout.
14225  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14226  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14227  * Each field definition object may contain the following properties: <ul>
14228  * <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,
14229  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14230  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14231  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14232  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14233  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14234  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14235  * this may be omitted.</p></li>
14236  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14237  * <ul><li>auto (Default, implies no conversion)</li>
14238  * <li>string</li>
14239  * <li>int</li>
14240  * <li>float</li>
14241  * <li>boolean</li>
14242  * <li>date</li></ul></p></li>
14243  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14244  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14245  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14246  * by the Reader into an object that will be stored in the Record. It is passed the
14247  * following parameters:<ul>
14248  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14249  * </ul></p></li>
14250  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14251  * </ul>
14252  * <br>usage:<br><pre><code>
14253 var TopicRecord = Roo.data.Record.create(
14254     {name: 'title', mapping: 'topic_title'},
14255     {name: 'author', mapping: 'username'},
14256     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14257     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14258     {name: 'lastPoster', mapping: 'user2'},
14259     {name: 'excerpt', mapping: 'post_text'}
14260 );
14261
14262 var myNewRecord = new TopicRecord({
14263     title: 'Do my job please',
14264     author: 'noobie',
14265     totalPosts: 1,
14266     lastPost: new Date(),
14267     lastPoster: 'Animal',
14268     excerpt: 'No way dude!'
14269 });
14270 myStore.add(myNewRecord);
14271 </code></pre>
14272  * @method create
14273  * @static
14274  */
14275 Roo.data.Record.create = function(o){
14276     var f = function(){
14277         f.superclass.constructor.apply(this, arguments);
14278     };
14279     Roo.extend(f, Roo.data.Record);
14280     var p = f.prototype;
14281     p.fields = new Roo.util.MixedCollection(false, function(field){
14282         return field.name;
14283     });
14284     for(var i = 0, len = o.length; i < len; i++){
14285         p.fields.add(new Roo.data.Field(o[i]));
14286     }
14287     f.getField = function(name){
14288         return p.fields.get(name);  
14289     };
14290     return f;
14291 };
14292
14293 Roo.data.Record.AUTO_ID = 1000;
14294 Roo.data.Record.EDIT = 'edit';
14295 Roo.data.Record.REJECT = 'reject';
14296 Roo.data.Record.COMMIT = 'commit';
14297
14298 Roo.data.Record.prototype = {
14299     /**
14300      * Readonly flag - true if this record has been modified.
14301      * @type Boolean
14302      */
14303     dirty : false,
14304     editing : false,
14305     error: null,
14306     modified: null,
14307
14308     // private
14309     join : function(store){
14310         this.store = store;
14311     },
14312
14313     /**
14314      * Set the named field to the specified value.
14315      * @param {String} name The name of the field to set.
14316      * @param {Object} value The value to set the field to.
14317      */
14318     set : function(name, value){
14319         if(this.data[name] == value){
14320             return;
14321         }
14322         this.dirty = true;
14323         if(!this.modified){
14324             this.modified = {};
14325         }
14326         if(typeof this.modified[name] == 'undefined'){
14327             this.modified[name] = this.data[name];
14328         }
14329         this.data[name] = value;
14330         if(!this.editing && this.store){
14331             this.store.afterEdit(this);
14332         }       
14333     },
14334
14335     /**
14336      * Get the value of the named field.
14337      * @param {String} name The name of the field to get the value of.
14338      * @return {Object} The value of the field.
14339      */
14340     get : function(name){
14341         return this.data[name]; 
14342     },
14343
14344     // private
14345     beginEdit : function(){
14346         this.editing = true;
14347         this.modified = {}; 
14348     },
14349
14350     // private
14351     cancelEdit : function(){
14352         this.editing = false;
14353         delete this.modified;
14354     },
14355
14356     // private
14357     endEdit : function(){
14358         this.editing = false;
14359         if(this.dirty && this.store){
14360             this.store.afterEdit(this);
14361         }
14362     },
14363
14364     /**
14365      * Usually called by the {@link Roo.data.Store} which owns the Record.
14366      * Rejects all changes made to the Record since either creation, or the last commit operation.
14367      * Modified fields are reverted to their original values.
14368      * <p>
14369      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14370      * of reject operations.
14371      */
14372     reject : function(){
14373         var m = this.modified;
14374         for(var n in m){
14375             if(typeof m[n] != "function"){
14376                 this.data[n] = m[n];
14377             }
14378         }
14379         this.dirty = false;
14380         delete this.modified;
14381         this.editing = false;
14382         if(this.store){
14383             this.store.afterReject(this);
14384         }
14385     },
14386
14387     /**
14388      * Usually called by the {@link Roo.data.Store} which owns the Record.
14389      * Commits all changes made to the Record since either creation, or the last commit operation.
14390      * <p>
14391      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14392      * of commit operations.
14393      */
14394     commit : function(){
14395         this.dirty = false;
14396         delete this.modified;
14397         this.editing = false;
14398         if(this.store){
14399             this.store.afterCommit(this);
14400         }
14401     },
14402
14403     // private
14404     hasError : function(){
14405         return this.error != null;
14406     },
14407
14408     // private
14409     clearError : function(){
14410         this.error = null;
14411     },
14412
14413     /**
14414      * Creates a copy of this record.
14415      * @param {String} id (optional) A new record id if you don't want to use this record's id
14416      * @return {Record}
14417      */
14418     copy : function(newId) {
14419         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14420     }
14421 };/*
14422  * Based on:
14423  * Ext JS Library 1.1.1
14424  * Copyright(c) 2006-2007, Ext JS, LLC.
14425  *
14426  * Originally Released Under LGPL - original licence link has changed is not relivant.
14427  *
14428  * Fork - LGPL
14429  * <script type="text/javascript">
14430  */
14431
14432
14433
14434 /**
14435  * @class Roo.data.Store
14436  * @extends Roo.util.Observable
14437  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14438  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14439  * <p>
14440  * 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
14441  * has no knowledge of the format of the data returned by the Proxy.<br>
14442  * <p>
14443  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14444  * instances from the data object. These records are cached and made available through accessor functions.
14445  * @constructor
14446  * Creates a new Store.
14447  * @param {Object} config A config object containing the objects needed for the Store to access data,
14448  * and read the data into Records.
14449  */
14450 Roo.data.Store = function(config){
14451     this.data = new Roo.util.MixedCollection(false);
14452     this.data.getKey = function(o){
14453         return o.id;
14454     };
14455     this.baseParams = {};
14456     // private
14457     this.paramNames = {
14458         "start" : "start",
14459         "limit" : "limit",
14460         "sort" : "sort",
14461         "dir" : "dir",
14462         "multisort" : "_multisort"
14463     };
14464
14465     if(config && config.data){
14466         this.inlineData = config.data;
14467         delete config.data;
14468     }
14469
14470     Roo.apply(this, config);
14471     
14472     if(this.reader){ // reader passed
14473         this.reader = Roo.factory(this.reader, Roo.data);
14474         this.reader.xmodule = this.xmodule || false;
14475         if(!this.recordType){
14476             this.recordType = this.reader.recordType;
14477         }
14478         if(this.reader.onMetaChange){
14479             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14480         }
14481     }
14482
14483     if(this.recordType){
14484         this.fields = this.recordType.prototype.fields;
14485     }
14486     this.modified = [];
14487
14488     this.addEvents({
14489         /**
14490          * @event datachanged
14491          * Fires when the data cache has changed, and a widget which is using this Store
14492          * as a Record cache should refresh its view.
14493          * @param {Store} this
14494          */
14495         datachanged : true,
14496         /**
14497          * @event metachange
14498          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14499          * @param {Store} this
14500          * @param {Object} meta The JSON metadata
14501          */
14502         metachange : true,
14503         /**
14504          * @event add
14505          * Fires when Records have been added to the Store
14506          * @param {Store} this
14507          * @param {Roo.data.Record[]} records The array of Records added
14508          * @param {Number} index The index at which the record(s) were added
14509          */
14510         add : true,
14511         /**
14512          * @event remove
14513          * Fires when a Record has been removed from the Store
14514          * @param {Store} this
14515          * @param {Roo.data.Record} record The Record that was removed
14516          * @param {Number} index The index at which the record was removed
14517          */
14518         remove : true,
14519         /**
14520          * @event update
14521          * Fires when a Record has been updated
14522          * @param {Store} this
14523          * @param {Roo.data.Record} record The Record that was updated
14524          * @param {String} operation The update operation being performed.  Value may be one of:
14525          * <pre><code>
14526  Roo.data.Record.EDIT
14527  Roo.data.Record.REJECT
14528  Roo.data.Record.COMMIT
14529          * </code></pre>
14530          */
14531         update : true,
14532         /**
14533          * @event clear
14534          * Fires when the data cache has been cleared.
14535          * @param {Store} this
14536          */
14537         clear : true,
14538         /**
14539          * @event beforeload
14540          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14541          * the load action will be canceled.
14542          * @param {Store} this
14543          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14544          */
14545         beforeload : true,
14546         /**
14547          * @event beforeloadadd
14548          * Fires after a new set of Records has been loaded.
14549          * @param {Store} this
14550          * @param {Roo.data.Record[]} records The Records that were loaded
14551          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14552          */
14553         beforeloadadd : true,
14554         /**
14555          * @event load
14556          * Fires after a new set of Records has been loaded, before they are added to the store.
14557          * @param {Store} this
14558          * @param {Roo.data.Record[]} records The Records that were loaded
14559          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14560          * @params {Object} return from reader
14561          */
14562         load : true,
14563         /**
14564          * @event loadexception
14565          * Fires if an exception occurs in the Proxy during loading.
14566          * Called with the signature of the Proxy's "loadexception" event.
14567          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14568          * 
14569          * @param {Proxy} 
14570          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14571          * @param {Object} load options 
14572          * @param {Object} jsonData from your request (normally this contains the Exception)
14573          */
14574         loadexception : true
14575     });
14576     
14577     if(this.proxy){
14578         this.proxy = Roo.factory(this.proxy, Roo.data);
14579         this.proxy.xmodule = this.xmodule || false;
14580         this.relayEvents(this.proxy,  ["loadexception"]);
14581     }
14582     this.sortToggle = {};
14583     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14584
14585     Roo.data.Store.superclass.constructor.call(this);
14586
14587     if(this.inlineData){
14588         this.loadData(this.inlineData);
14589         delete this.inlineData;
14590     }
14591 };
14592
14593 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14594      /**
14595     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14596     * without a remote query - used by combo/forms at present.
14597     */
14598     
14599     /**
14600     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14601     */
14602     /**
14603     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14604     */
14605     /**
14606     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14607     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14608     */
14609     /**
14610     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14611     * on any HTTP request
14612     */
14613     /**
14614     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14615     */
14616     /**
14617     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14618     */
14619     multiSort: false,
14620     /**
14621     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14622     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14623     */
14624     remoteSort : false,
14625
14626     /**
14627     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14628      * loaded or when a record is removed. (defaults to false).
14629     */
14630     pruneModifiedRecords : false,
14631
14632     // private
14633     lastOptions : null,
14634
14635     /**
14636      * Add Records to the Store and fires the add event.
14637      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14638      */
14639     add : function(records){
14640         records = [].concat(records);
14641         for(var i = 0, len = records.length; i < len; i++){
14642             records[i].join(this);
14643         }
14644         var index = this.data.length;
14645         this.data.addAll(records);
14646         this.fireEvent("add", this, records, index);
14647     },
14648
14649     /**
14650      * Remove a Record from the Store and fires the remove event.
14651      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14652      */
14653     remove : function(record){
14654         var index = this.data.indexOf(record);
14655         this.data.removeAt(index);
14656  
14657         if(this.pruneModifiedRecords){
14658             this.modified.remove(record);
14659         }
14660         this.fireEvent("remove", this, record, index);
14661     },
14662
14663     /**
14664      * Remove all Records from the Store and fires the clear event.
14665      */
14666     removeAll : function(){
14667         this.data.clear();
14668         if(this.pruneModifiedRecords){
14669             this.modified = [];
14670         }
14671         this.fireEvent("clear", this);
14672     },
14673
14674     /**
14675      * Inserts Records to the Store at the given index and fires the add event.
14676      * @param {Number} index The start index at which to insert the passed Records.
14677      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14678      */
14679     insert : function(index, records){
14680         records = [].concat(records);
14681         for(var i = 0, len = records.length; i < len; i++){
14682             this.data.insert(index, records[i]);
14683             records[i].join(this);
14684         }
14685         this.fireEvent("add", this, records, index);
14686     },
14687
14688     /**
14689      * Get the index within the cache of the passed Record.
14690      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14691      * @return {Number} The index of the passed Record. Returns -1 if not found.
14692      */
14693     indexOf : function(record){
14694         return this.data.indexOf(record);
14695     },
14696
14697     /**
14698      * Get the index within the cache of the Record with the passed id.
14699      * @param {String} id The id of the Record to find.
14700      * @return {Number} The index of the Record. Returns -1 if not found.
14701      */
14702     indexOfId : function(id){
14703         return this.data.indexOfKey(id);
14704     },
14705
14706     /**
14707      * Get the Record with the specified id.
14708      * @param {String} id The id of the Record to find.
14709      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14710      */
14711     getById : function(id){
14712         return this.data.key(id);
14713     },
14714
14715     /**
14716      * Get the Record at the specified index.
14717      * @param {Number} index The index of the Record to find.
14718      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14719      */
14720     getAt : function(index){
14721         return this.data.itemAt(index);
14722     },
14723
14724     /**
14725      * Returns a range of Records between specified indices.
14726      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14727      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14728      * @return {Roo.data.Record[]} An array of Records
14729      */
14730     getRange : function(start, end){
14731         return this.data.getRange(start, end);
14732     },
14733
14734     // private
14735     storeOptions : function(o){
14736         o = Roo.apply({}, o);
14737         delete o.callback;
14738         delete o.scope;
14739         this.lastOptions = o;
14740     },
14741
14742     /**
14743      * Loads the Record cache from the configured Proxy using the configured Reader.
14744      * <p>
14745      * If using remote paging, then the first load call must specify the <em>start</em>
14746      * and <em>limit</em> properties in the options.params property to establish the initial
14747      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14748      * <p>
14749      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14750      * and this call will return before the new data has been loaded. Perform any post-processing
14751      * in a callback function, or in a "load" event handler.</strong>
14752      * <p>
14753      * @param {Object} options An object containing properties which control loading options:<ul>
14754      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14755      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14756      * passed the following arguments:<ul>
14757      * <li>r : Roo.data.Record[]</li>
14758      * <li>options: Options object from the load call</li>
14759      * <li>success: Boolean success indicator</li></ul></li>
14760      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14761      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14762      * </ul>
14763      */
14764     load : function(options){
14765         options = options || {};
14766         if(this.fireEvent("beforeload", this, options) !== false){
14767             this.storeOptions(options);
14768             var p = Roo.apply(options.params || {}, this.baseParams);
14769             // if meta was not loaded from remote source.. try requesting it.
14770             if (!this.reader.metaFromRemote) {
14771                 p._requestMeta = 1;
14772             }
14773             if(this.sortInfo && this.remoteSort){
14774                 var pn = this.paramNames;
14775                 p[pn["sort"]] = this.sortInfo.field;
14776                 p[pn["dir"]] = this.sortInfo.direction;
14777             }
14778             if (this.multiSort) {
14779                 var pn = this.paramNames;
14780                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14781             }
14782             
14783             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14784         }
14785     },
14786
14787     /**
14788      * Reloads the Record cache from the configured Proxy using the configured Reader and
14789      * the options from the last load operation performed.
14790      * @param {Object} options (optional) An object containing properties which may override the options
14791      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14792      * the most recently used options are reused).
14793      */
14794     reload : function(options){
14795         this.load(Roo.applyIf(options||{}, this.lastOptions));
14796     },
14797
14798     // private
14799     // Called as a callback by the Reader during a load operation.
14800     loadRecords : function(o, options, success){
14801         if(!o || success === false){
14802             if(success !== false){
14803                 this.fireEvent("load", this, [], options, o);
14804             }
14805             if(options.callback){
14806                 options.callback.call(options.scope || this, [], options, false);
14807             }
14808             return;
14809         }
14810         // if data returned failure - throw an exception.
14811         if (o.success === false) {
14812             // show a message if no listener is registered.
14813             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14814                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14815             }
14816             // loadmask wil be hooked into this..
14817             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14818             return;
14819         }
14820         var r = o.records, t = o.totalRecords || r.length;
14821         
14822         this.fireEvent("beforeloadadd", this, r, options, o);
14823         
14824         if(!options || options.add !== true){
14825             if(this.pruneModifiedRecords){
14826                 this.modified = [];
14827             }
14828             for(var i = 0, len = r.length; i < len; i++){
14829                 r[i].join(this);
14830             }
14831             if(this.snapshot){
14832                 this.data = this.snapshot;
14833                 delete this.snapshot;
14834             }
14835             this.data.clear();
14836             this.data.addAll(r);
14837             this.totalLength = t;
14838             this.applySort();
14839             this.fireEvent("datachanged", this);
14840         }else{
14841             this.totalLength = Math.max(t, this.data.length+r.length);
14842             this.add(r);
14843         }
14844         
14845         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14846                 
14847             var e = new Roo.data.Record({});
14848
14849             e.set(this.parent.displayField, this.parent.emptyTitle);
14850             e.set(this.parent.valueField, '');
14851
14852             this.insert(0, e);
14853         }
14854             
14855         this.fireEvent("load", this, r, options, o);
14856         if(options.callback){
14857             options.callback.call(options.scope || this, r, options, true);
14858         }
14859     },
14860
14861
14862     /**
14863      * Loads data from a passed data block. A Reader which understands the format of the data
14864      * must have been configured in the constructor.
14865      * @param {Object} data The data block from which to read the Records.  The format of the data expected
14866      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14867      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14868      */
14869     loadData : function(o, append){
14870         var r = this.reader.readRecords(o);
14871         this.loadRecords(r, {add: append}, true);
14872     },
14873     
14874      /**
14875      * using 'cn' the nested child reader read the child array into it's child stores.
14876      * @param {Object} rec The record with a 'children array
14877      */
14878     loadDataFromChildren : function(rec)
14879     {
14880         this.loadData(this.reader.toLoadData(rec));
14881     },
14882     
14883
14884     /**
14885      * Gets the number of cached records.
14886      * <p>
14887      * <em>If using paging, this may not be the total size of the dataset. If the data object
14888      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14889      * the data set size</em>
14890      */
14891     getCount : function(){
14892         return this.data.length || 0;
14893     },
14894
14895     /**
14896      * Gets the total number of records in the dataset as returned by the server.
14897      * <p>
14898      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14899      * the dataset size</em>
14900      */
14901     getTotalCount : function(){
14902         return this.totalLength || 0;
14903     },
14904
14905     /**
14906      * Returns the sort state of the Store as an object with two properties:
14907      * <pre><code>
14908  field {String} The name of the field by which the Records are sorted
14909  direction {String} The sort order, "ASC" or "DESC"
14910      * </code></pre>
14911      */
14912     getSortState : function(){
14913         return this.sortInfo;
14914     },
14915
14916     // private
14917     applySort : function(){
14918         if(this.sortInfo && !this.remoteSort){
14919             var s = this.sortInfo, f = s.field;
14920             var st = this.fields.get(f).sortType;
14921             var fn = function(r1, r2){
14922                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14923                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14924             };
14925             this.data.sort(s.direction, fn);
14926             if(this.snapshot && this.snapshot != this.data){
14927                 this.snapshot.sort(s.direction, fn);
14928             }
14929         }
14930     },
14931
14932     /**
14933      * Sets the default sort column and order to be used by the next load operation.
14934      * @param {String} fieldName The name of the field to sort by.
14935      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14936      */
14937     setDefaultSort : function(field, dir){
14938         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14939     },
14940
14941     /**
14942      * Sort the Records.
14943      * If remote sorting is used, the sort is performed on the server, and the cache is
14944      * reloaded. If local sorting is used, the cache is sorted internally.
14945      * @param {String} fieldName The name of the field to sort by.
14946      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14947      */
14948     sort : function(fieldName, dir){
14949         var f = this.fields.get(fieldName);
14950         if(!dir){
14951             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14952             
14953             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14954                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14955             }else{
14956                 dir = f.sortDir;
14957             }
14958         }
14959         this.sortToggle[f.name] = dir;
14960         this.sortInfo = {field: f.name, direction: dir};
14961         if(!this.remoteSort){
14962             this.applySort();
14963             this.fireEvent("datachanged", this);
14964         }else{
14965             this.load(this.lastOptions);
14966         }
14967     },
14968
14969     /**
14970      * Calls the specified function for each of the Records in the cache.
14971      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14972      * Returning <em>false</em> aborts and exits the iteration.
14973      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14974      */
14975     each : function(fn, scope){
14976         this.data.each(fn, scope);
14977     },
14978
14979     /**
14980      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14981      * (e.g., during paging).
14982      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14983      */
14984     getModifiedRecords : function(){
14985         return this.modified;
14986     },
14987
14988     // private
14989     createFilterFn : function(property, value, anyMatch){
14990         if(!value.exec){ // not a regex
14991             value = String(value);
14992             if(value.length == 0){
14993                 return false;
14994             }
14995             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14996         }
14997         return function(r){
14998             return value.test(r.data[property]);
14999         };
15000     },
15001
15002     /**
15003      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15004      * @param {String} property A field on your records
15005      * @param {Number} start The record index to start at (defaults to 0)
15006      * @param {Number} end The last record index to include (defaults to length - 1)
15007      * @return {Number} The sum
15008      */
15009     sum : function(property, start, end){
15010         var rs = this.data.items, v = 0;
15011         start = start || 0;
15012         end = (end || end === 0) ? end : rs.length-1;
15013
15014         for(var i = start; i <= end; i++){
15015             v += (rs[i].data[property] || 0);
15016         }
15017         return v;
15018     },
15019
15020     /**
15021      * Filter the records by a specified property.
15022      * @param {String} field A field on your records
15023      * @param {String/RegExp} value Either a string that the field
15024      * should start with or a RegExp to test against the field
15025      * @param {Boolean} anyMatch True to match any part not just the beginning
15026      */
15027     filter : function(property, value, anyMatch){
15028         var fn = this.createFilterFn(property, value, anyMatch);
15029         return fn ? this.filterBy(fn) : this.clearFilter();
15030     },
15031
15032     /**
15033      * Filter by a function. The specified function will be called with each
15034      * record in this data source. If the function returns true the record is included,
15035      * otherwise it is filtered.
15036      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15037      * @param {Object} scope (optional) The scope of the function (defaults to this)
15038      */
15039     filterBy : function(fn, scope){
15040         this.snapshot = this.snapshot || this.data;
15041         this.data = this.queryBy(fn, scope||this);
15042         this.fireEvent("datachanged", this);
15043     },
15044
15045     /**
15046      * Query the records by a specified property.
15047      * @param {String} field A field on your records
15048      * @param {String/RegExp} value Either a string that the field
15049      * should start with or a RegExp to test against the field
15050      * @param {Boolean} anyMatch True to match any part not just the beginning
15051      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15052      */
15053     query : function(property, value, anyMatch){
15054         var fn = this.createFilterFn(property, value, anyMatch);
15055         return fn ? this.queryBy(fn) : this.data.clone();
15056     },
15057
15058     /**
15059      * Query by a function. The specified function will be called with each
15060      * record in this data source. If the function returns true the record is included
15061      * in the results.
15062      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15063      * @param {Object} scope (optional) The scope of the function (defaults to this)
15064       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15065      **/
15066     queryBy : function(fn, scope){
15067         var data = this.snapshot || this.data;
15068         return data.filterBy(fn, scope||this);
15069     },
15070
15071     /**
15072      * Collects unique values for a particular dataIndex from this store.
15073      * @param {String} dataIndex The property to collect
15074      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15075      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15076      * @return {Array} An array of the unique values
15077      **/
15078     collect : function(dataIndex, allowNull, bypassFilter){
15079         var d = (bypassFilter === true && this.snapshot) ?
15080                 this.snapshot.items : this.data.items;
15081         var v, sv, r = [], l = {};
15082         for(var i = 0, len = d.length; i < len; i++){
15083             v = d[i].data[dataIndex];
15084             sv = String(v);
15085             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15086                 l[sv] = true;
15087                 r[r.length] = v;
15088             }
15089         }
15090         return r;
15091     },
15092
15093     /**
15094      * Revert to a view of the Record cache with no filtering applied.
15095      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15096      */
15097     clearFilter : function(suppressEvent){
15098         if(this.snapshot && this.snapshot != this.data){
15099             this.data = this.snapshot;
15100             delete this.snapshot;
15101             if(suppressEvent !== true){
15102                 this.fireEvent("datachanged", this);
15103             }
15104         }
15105     },
15106
15107     // private
15108     afterEdit : function(record){
15109         if(this.modified.indexOf(record) == -1){
15110             this.modified.push(record);
15111         }
15112         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15113     },
15114     
15115     // private
15116     afterReject : function(record){
15117         this.modified.remove(record);
15118         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15119     },
15120
15121     // private
15122     afterCommit : function(record){
15123         this.modified.remove(record);
15124         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15125     },
15126
15127     /**
15128      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15129      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15130      */
15131     commitChanges : function(){
15132         var m = this.modified.slice(0);
15133         this.modified = [];
15134         for(var i = 0, len = m.length; i < len; i++){
15135             m[i].commit();
15136         }
15137     },
15138
15139     /**
15140      * Cancel outstanding changes on all changed records.
15141      */
15142     rejectChanges : function(){
15143         var m = this.modified.slice(0);
15144         this.modified = [];
15145         for(var i = 0, len = m.length; i < len; i++){
15146             m[i].reject();
15147         }
15148     },
15149
15150     onMetaChange : function(meta, rtype, o){
15151         this.recordType = rtype;
15152         this.fields = rtype.prototype.fields;
15153         delete this.snapshot;
15154         this.sortInfo = meta.sortInfo || this.sortInfo;
15155         this.modified = [];
15156         this.fireEvent('metachange', this, this.reader.meta);
15157     },
15158     
15159     moveIndex : function(data, type)
15160     {
15161         var index = this.indexOf(data);
15162         
15163         var newIndex = index + type;
15164         
15165         this.remove(data);
15166         
15167         this.insert(newIndex, data);
15168         
15169     }
15170 });/*
15171  * Based on:
15172  * Ext JS Library 1.1.1
15173  * Copyright(c) 2006-2007, Ext JS, LLC.
15174  *
15175  * Originally Released Under LGPL - original licence link has changed is not relivant.
15176  *
15177  * Fork - LGPL
15178  * <script type="text/javascript">
15179  */
15180
15181 /**
15182  * @class Roo.data.SimpleStore
15183  * @extends Roo.data.Store
15184  * Small helper class to make creating Stores from Array data easier.
15185  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15186  * @cfg {Array} fields An array of field definition objects, or field name strings.
15187  * @cfg {Object} an existing reader (eg. copied from another store)
15188  * @cfg {Array} data The multi-dimensional array of data
15189  * @constructor
15190  * @param {Object} config
15191  */
15192 Roo.data.SimpleStore = function(config)
15193 {
15194     Roo.data.SimpleStore.superclass.constructor.call(this, {
15195         isLocal : true,
15196         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15197                 id: config.id
15198             },
15199             Roo.data.Record.create(config.fields)
15200         ),
15201         proxy : new Roo.data.MemoryProxy(config.data)
15202     });
15203     this.load();
15204 };
15205 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15206  * Based on:
15207  * Ext JS Library 1.1.1
15208  * Copyright(c) 2006-2007, Ext JS, LLC.
15209  *
15210  * Originally Released Under LGPL - original licence link has changed is not relivant.
15211  *
15212  * Fork - LGPL
15213  * <script type="text/javascript">
15214  */
15215
15216 /**
15217 /**
15218  * @extends Roo.data.Store
15219  * @class Roo.data.JsonStore
15220  * Small helper class to make creating Stores for JSON data easier. <br/>
15221 <pre><code>
15222 var store = new Roo.data.JsonStore({
15223     url: 'get-images.php',
15224     root: 'images',
15225     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15226 });
15227 </code></pre>
15228  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15229  * JsonReader and HttpProxy (unless inline data is provided).</b>
15230  * @cfg {Array} fields An array of field definition objects, or field name strings.
15231  * @constructor
15232  * @param {Object} config
15233  */
15234 Roo.data.JsonStore = function(c){
15235     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15236         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15237         reader: new Roo.data.JsonReader(c, c.fields)
15238     }));
15239 };
15240 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15241  * Based on:
15242  * Ext JS Library 1.1.1
15243  * Copyright(c) 2006-2007, Ext JS, LLC.
15244  *
15245  * Originally Released Under LGPL - original licence link has changed is not relivant.
15246  *
15247  * Fork - LGPL
15248  * <script type="text/javascript">
15249  */
15250
15251  
15252 Roo.data.Field = function(config){
15253     if(typeof config == "string"){
15254         config = {name: config};
15255     }
15256     Roo.apply(this, config);
15257     
15258     if(!this.type){
15259         this.type = "auto";
15260     }
15261     
15262     var st = Roo.data.SortTypes;
15263     // named sortTypes are supported, here we look them up
15264     if(typeof this.sortType == "string"){
15265         this.sortType = st[this.sortType];
15266     }
15267     
15268     // set default sortType for strings and dates
15269     if(!this.sortType){
15270         switch(this.type){
15271             case "string":
15272                 this.sortType = st.asUCString;
15273                 break;
15274             case "date":
15275                 this.sortType = st.asDate;
15276                 break;
15277             default:
15278                 this.sortType = st.none;
15279         }
15280     }
15281
15282     // define once
15283     var stripRe = /[\$,%]/g;
15284
15285     // prebuilt conversion function for this field, instead of
15286     // switching every time we're reading a value
15287     if(!this.convert){
15288         var cv, dateFormat = this.dateFormat;
15289         switch(this.type){
15290             case "":
15291             case "auto":
15292             case undefined:
15293                 cv = function(v){ return v; };
15294                 break;
15295             case "string":
15296                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15297                 break;
15298             case "int":
15299                 cv = function(v){
15300                     return v !== undefined && v !== null && v !== '' ?
15301                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15302                     };
15303                 break;
15304             case "float":
15305                 cv = function(v){
15306                     return v !== undefined && v !== null && v !== '' ?
15307                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15308                     };
15309                 break;
15310             case "bool":
15311             case "boolean":
15312                 cv = function(v){ return v === true || v === "true" || v == 1; };
15313                 break;
15314             case "date":
15315                 cv = function(v){
15316                     if(!v){
15317                         return '';
15318                     }
15319                     if(v instanceof Date){
15320                         return v;
15321                     }
15322                     if(dateFormat){
15323                         if(dateFormat == "timestamp"){
15324                             return new Date(v*1000);
15325                         }
15326                         return Date.parseDate(v, dateFormat);
15327                     }
15328                     var parsed = Date.parse(v);
15329                     return parsed ? new Date(parsed) : null;
15330                 };
15331              break;
15332             
15333         }
15334         this.convert = cv;
15335     }
15336 };
15337
15338 Roo.data.Field.prototype = {
15339     dateFormat: null,
15340     defaultValue: "",
15341     mapping: null,
15342     sortType : null,
15343     sortDir : "ASC"
15344 };/*
15345  * Based on:
15346  * Ext JS Library 1.1.1
15347  * Copyright(c) 2006-2007, Ext JS, LLC.
15348  *
15349  * Originally Released Under LGPL - original licence link has changed is not relivant.
15350  *
15351  * Fork - LGPL
15352  * <script type="text/javascript">
15353  */
15354  
15355 // Base class for reading structured data from a data source.  This class is intended to be
15356 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15357
15358 /**
15359  * @class Roo.data.DataReader
15360  * Base class for reading structured data from a data source.  This class is intended to be
15361  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15362  */
15363
15364 Roo.data.DataReader = function(meta, recordType){
15365     
15366     this.meta = meta;
15367     
15368     this.recordType = recordType instanceof Array ? 
15369         Roo.data.Record.create(recordType) : recordType;
15370 };
15371
15372 Roo.data.DataReader.prototype = {
15373     
15374     
15375     readerType : 'Data',
15376      /**
15377      * Create an empty record
15378      * @param {Object} data (optional) - overlay some values
15379      * @return {Roo.data.Record} record created.
15380      */
15381     newRow :  function(d) {
15382         var da =  {};
15383         this.recordType.prototype.fields.each(function(c) {
15384             switch( c.type) {
15385                 case 'int' : da[c.name] = 0; break;
15386                 case 'date' : da[c.name] = new Date(); break;
15387                 case 'float' : da[c.name] = 0.0; break;
15388                 case 'boolean' : da[c.name] = false; break;
15389                 default : da[c.name] = ""; break;
15390             }
15391             
15392         });
15393         return new this.recordType(Roo.apply(da, d));
15394     }
15395     
15396     
15397 };/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408 /**
15409  * @class Roo.data.DataProxy
15410  * @extends Roo.data.Observable
15411  * This class is an abstract base class for implementations which provide retrieval of
15412  * unformatted data objects.<br>
15413  * <p>
15414  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15415  * (of the appropriate type which knows how to parse the data object) to provide a block of
15416  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15417  * <p>
15418  * Custom implementations must implement the load method as described in
15419  * {@link Roo.data.HttpProxy#load}.
15420  */
15421 Roo.data.DataProxy = function(){
15422     this.addEvents({
15423         /**
15424          * @event beforeload
15425          * Fires before a network request is made to retrieve a data object.
15426          * @param {Object} This DataProxy object.
15427          * @param {Object} params The params parameter to the load function.
15428          */
15429         beforeload : true,
15430         /**
15431          * @event load
15432          * Fires before the load method's callback is called.
15433          * @param {Object} This DataProxy object.
15434          * @param {Object} o The data object.
15435          * @param {Object} arg The callback argument object passed to the load function.
15436          */
15437         load : true,
15438         /**
15439          * @event loadexception
15440          * Fires if an Exception occurs during data retrieval.
15441          * @param {Object} This DataProxy object.
15442          * @param {Object} o The data object.
15443          * @param {Object} arg The callback argument object passed to the load function.
15444          * @param {Object} e The Exception.
15445          */
15446         loadexception : true
15447     });
15448     Roo.data.DataProxy.superclass.constructor.call(this);
15449 };
15450
15451 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15452
15453     /**
15454      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15455      */
15456 /*
15457  * Based on:
15458  * Ext JS Library 1.1.1
15459  * Copyright(c) 2006-2007, Ext JS, LLC.
15460  *
15461  * Originally Released Under LGPL - original licence link has changed is not relivant.
15462  *
15463  * Fork - LGPL
15464  * <script type="text/javascript">
15465  */
15466 /**
15467  * @class Roo.data.MemoryProxy
15468  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15469  * to the Reader when its load method is called.
15470  * @constructor
15471  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15472  */
15473 Roo.data.MemoryProxy = function(data){
15474     if (data.data) {
15475         data = data.data;
15476     }
15477     Roo.data.MemoryProxy.superclass.constructor.call(this);
15478     this.data = data;
15479 };
15480
15481 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15482     
15483     /**
15484      * Load data from the requested source (in this case an in-memory
15485      * data object passed to the constructor), read the data object into
15486      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15487      * process that block using the passed callback.
15488      * @param {Object} params This parameter is not used by the MemoryProxy class.
15489      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15490      * object into a block of Roo.data.Records.
15491      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15492      * The function must be passed <ul>
15493      * <li>The Record block object</li>
15494      * <li>The "arg" argument from the load function</li>
15495      * <li>A boolean success indicator</li>
15496      * </ul>
15497      * @param {Object} scope The scope in which to call the callback
15498      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15499      */
15500     load : function(params, reader, callback, scope, arg){
15501         params = params || {};
15502         var result;
15503         try {
15504             result = reader.readRecords(params.data ? params.data :this.data);
15505         }catch(e){
15506             this.fireEvent("loadexception", this, arg, null, e);
15507             callback.call(scope, null, arg, false);
15508             return;
15509         }
15510         callback.call(scope, result, arg, true);
15511     },
15512     
15513     // private
15514     update : function(params, records){
15515         
15516     }
15517 });/*
15518  * Based on:
15519  * Ext JS Library 1.1.1
15520  * Copyright(c) 2006-2007, Ext JS, LLC.
15521  *
15522  * Originally Released Under LGPL - original licence link has changed is not relivant.
15523  *
15524  * Fork - LGPL
15525  * <script type="text/javascript">
15526  */
15527 /**
15528  * @class Roo.data.HttpProxy
15529  * @extends Roo.data.DataProxy
15530  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15531  * configured to reference a certain URL.<br><br>
15532  * <p>
15533  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15534  * from which the running page was served.<br><br>
15535  * <p>
15536  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15537  * <p>
15538  * Be aware that to enable the browser to parse an XML document, the server must set
15539  * the Content-Type header in the HTTP response to "text/xml".
15540  * @constructor
15541  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15542  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15543  * will be used to make the request.
15544  */
15545 Roo.data.HttpProxy = function(conn){
15546     Roo.data.HttpProxy.superclass.constructor.call(this);
15547     // is conn a conn config or a real conn?
15548     this.conn = conn;
15549     this.useAjax = !conn || !conn.events;
15550   
15551 };
15552
15553 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15554     // thse are take from connection...
15555     
15556     /**
15557      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15558      */
15559     /**
15560      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15561      * extra parameters to each request made by this object. (defaults to undefined)
15562      */
15563     /**
15564      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15565      *  to each request made by this object. (defaults to undefined)
15566      */
15567     /**
15568      * @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)
15569      */
15570     /**
15571      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15572      */
15573      /**
15574      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15575      * @type Boolean
15576      */
15577   
15578
15579     /**
15580      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15581      * @type Boolean
15582      */
15583     /**
15584      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15585      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15586      * a finer-grained basis than the DataProxy events.
15587      */
15588     getConnection : function(){
15589         return this.useAjax ? Roo.Ajax : this.conn;
15590     },
15591
15592     /**
15593      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15594      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15595      * process that block using the passed callback.
15596      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15597      * for the request to the remote server.
15598      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15599      * object into a block of Roo.data.Records.
15600      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15601      * The function must be passed <ul>
15602      * <li>The Record block object</li>
15603      * <li>The "arg" argument from the load function</li>
15604      * <li>A boolean success indicator</li>
15605      * </ul>
15606      * @param {Object} scope The scope in which to call the callback
15607      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15608      */
15609     load : function(params, reader, callback, scope, arg){
15610         if(this.fireEvent("beforeload", this, params) !== false){
15611             var  o = {
15612                 params : params || {},
15613                 request: {
15614                     callback : callback,
15615                     scope : scope,
15616                     arg : arg
15617                 },
15618                 reader: reader,
15619                 callback : this.loadResponse,
15620                 scope: this
15621             };
15622             if(this.useAjax){
15623                 Roo.applyIf(o, this.conn);
15624                 if(this.activeRequest){
15625                     Roo.Ajax.abort(this.activeRequest);
15626                 }
15627                 this.activeRequest = Roo.Ajax.request(o);
15628             }else{
15629                 this.conn.request(o);
15630             }
15631         }else{
15632             callback.call(scope||this, null, arg, false);
15633         }
15634     },
15635
15636     // private
15637     loadResponse : function(o, success, response){
15638         delete this.activeRequest;
15639         if(!success){
15640             this.fireEvent("loadexception", this, o, response);
15641             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15642             return;
15643         }
15644         var result;
15645         try {
15646             result = o.reader.read(response);
15647         }catch(e){
15648             this.fireEvent("loadexception", this, o, response, e);
15649             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15650             return;
15651         }
15652         
15653         this.fireEvent("load", this, o, o.request.arg);
15654         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15655     },
15656
15657     // private
15658     update : function(dataSet){
15659
15660     },
15661
15662     // private
15663     updateResponse : function(dataSet){
15664
15665     }
15666 });/*
15667  * Based on:
15668  * Ext JS Library 1.1.1
15669  * Copyright(c) 2006-2007, Ext JS, LLC.
15670  *
15671  * Originally Released Under LGPL - original licence link has changed is not relivant.
15672  *
15673  * Fork - LGPL
15674  * <script type="text/javascript">
15675  */
15676
15677 /**
15678  * @class Roo.data.ScriptTagProxy
15679  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15680  * other than the originating domain of the running page.<br><br>
15681  * <p>
15682  * <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
15683  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15684  * <p>
15685  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15686  * source code that is used as the source inside a &lt;script> tag.<br><br>
15687  * <p>
15688  * In order for the browser to process the returned data, the server must wrap the data object
15689  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15690  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15691  * depending on whether the callback name was passed:
15692  * <p>
15693  * <pre><code>
15694 boolean scriptTag = false;
15695 String cb = request.getParameter("callback");
15696 if (cb != null) {
15697     scriptTag = true;
15698     response.setContentType("text/javascript");
15699 } else {
15700     response.setContentType("application/x-json");
15701 }
15702 Writer out = response.getWriter();
15703 if (scriptTag) {
15704     out.write(cb + "(");
15705 }
15706 out.print(dataBlock.toJsonString());
15707 if (scriptTag) {
15708     out.write(");");
15709 }
15710 </pre></code>
15711  *
15712  * @constructor
15713  * @param {Object} config A configuration object.
15714  */
15715 Roo.data.ScriptTagProxy = function(config){
15716     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15717     Roo.apply(this, config);
15718     this.head = document.getElementsByTagName("head")[0];
15719 };
15720
15721 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15722
15723 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15724     /**
15725      * @cfg {String} url The URL from which to request the data object.
15726      */
15727     /**
15728      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15729      */
15730     timeout : 30000,
15731     /**
15732      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15733      * the server the name of the callback function set up by the load call to process the returned data object.
15734      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15735      * javascript output which calls this named function passing the data object as its only parameter.
15736      */
15737     callbackParam : "callback",
15738     /**
15739      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15740      * name to the request.
15741      */
15742     nocache : true,
15743
15744     /**
15745      * Load data from the configured URL, read the data object into
15746      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15747      * process that block using the passed callback.
15748      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15749      * for the request to the remote server.
15750      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15751      * object into a block of Roo.data.Records.
15752      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15753      * The function must be passed <ul>
15754      * <li>The Record block object</li>
15755      * <li>The "arg" argument from the load function</li>
15756      * <li>A boolean success indicator</li>
15757      * </ul>
15758      * @param {Object} scope The scope in which to call the callback
15759      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15760      */
15761     load : function(params, reader, callback, scope, arg){
15762         if(this.fireEvent("beforeload", this, params) !== false){
15763
15764             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15765
15766             var url = this.url;
15767             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15768             if(this.nocache){
15769                 url += "&_dc=" + (new Date().getTime());
15770             }
15771             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15772             var trans = {
15773                 id : transId,
15774                 cb : "stcCallback"+transId,
15775                 scriptId : "stcScript"+transId,
15776                 params : params,
15777                 arg : arg,
15778                 url : url,
15779                 callback : callback,
15780                 scope : scope,
15781                 reader : reader
15782             };
15783             var conn = this;
15784
15785             window[trans.cb] = function(o){
15786                 conn.handleResponse(o, trans);
15787             };
15788
15789             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15790
15791             if(this.autoAbort !== false){
15792                 this.abort();
15793             }
15794
15795             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15796
15797             var script = document.createElement("script");
15798             script.setAttribute("src", url);
15799             script.setAttribute("type", "text/javascript");
15800             script.setAttribute("id", trans.scriptId);
15801             this.head.appendChild(script);
15802
15803             this.trans = trans;
15804         }else{
15805             callback.call(scope||this, null, arg, false);
15806         }
15807     },
15808
15809     // private
15810     isLoading : function(){
15811         return this.trans ? true : false;
15812     },
15813
15814     /**
15815      * Abort the current server request.
15816      */
15817     abort : function(){
15818         if(this.isLoading()){
15819             this.destroyTrans(this.trans);
15820         }
15821     },
15822
15823     // private
15824     destroyTrans : function(trans, isLoaded){
15825         this.head.removeChild(document.getElementById(trans.scriptId));
15826         clearTimeout(trans.timeoutId);
15827         if(isLoaded){
15828             window[trans.cb] = undefined;
15829             try{
15830                 delete window[trans.cb];
15831             }catch(e){}
15832         }else{
15833             // if hasn't been loaded, wait for load to remove it to prevent script error
15834             window[trans.cb] = function(){
15835                 window[trans.cb] = undefined;
15836                 try{
15837                     delete window[trans.cb];
15838                 }catch(e){}
15839             };
15840         }
15841     },
15842
15843     // private
15844     handleResponse : function(o, trans){
15845         this.trans = false;
15846         this.destroyTrans(trans, true);
15847         var result;
15848         try {
15849             result = trans.reader.readRecords(o);
15850         }catch(e){
15851             this.fireEvent("loadexception", this, o, trans.arg, e);
15852             trans.callback.call(trans.scope||window, null, trans.arg, false);
15853             return;
15854         }
15855         this.fireEvent("load", this, o, trans.arg);
15856         trans.callback.call(trans.scope||window, result, trans.arg, true);
15857     },
15858
15859     // private
15860     handleFailure : function(trans){
15861         this.trans = false;
15862         this.destroyTrans(trans, false);
15863         this.fireEvent("loadexception", this, null, trans.arg);
15864         trans.callback.call(trans.scope||window, null, trans.arg, false);
15865     }
15866 });/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877 /**
15878  * @class Roo.data.JsonReader
15879  * @extends Roo.data.DataReader
15880  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15881  * based on mappings in a provided Roo.data.Record constructor.
15882  * 
15883  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15884  * in the reply previously. 
15885  * 
15886  * <p>
15887  * Example code:
15888  * <pre><code>
15889 var RecordDef = Roo.data.Record.create([
15890     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15891     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15892 ]);
15893 var myReader = new Roo.data.JsonReader({
15894     totalProperty: "results",    // The property which contains the total dataset size (optional)
15895     root: "rows",                // The property which contains an Array of row objects
15896     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15897 }, RecordDef);
15898 </code></pre>
15899  * <p>
15900  * This would consume a JSON file like this:
15901  * <pre><code>
15902 { 'results': 2, 'rows': [
15903     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15904     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15905 }
15906 </code></pre>
15907  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15908  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15909  * paged from the remote server.
15910  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15911  * @cfg {String} root name of the property which contains the Array of row objects.
15912  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15913  * @cfg {Array} fields Array of field definition objects
15914  * @constructor
15915  * Create a new JsonReader
15916  * @param {Object} meta Metadata configuration options
15917  * @param {Object} recordType Either an Array of field definition objects,
15918  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15919  */
15920 Roo.data.JsonReader = function(meta, recordType){
15921     
15922     meta = meta || {};
15923     // set some defaults:
15924     Roo.applyIf(meta, {
15925         totalProperty: 'total',
15926         successProperty : 'success',
15927         root : 'data',
15928         id : 'id'
15929     });
15930     
15931     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15932 };
15933 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15934     
15935     readerType : 'Json',
15936     
15937     /**
15938      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15939      * Used by Store query builder to append _requestMeta to params.
15940      * 
15941      */
15942     metaFromRemote : false,
15943     /**
15944      * This method is only used by a DataProxy which has retrieved data from a remote server.
15945      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15946      * @return {Object} data A data block which is used by an Roo.data.Store object as
15947      * a cache of Roo.data.Records.
15948      */
15949     read : function(response){
15950         var json = response.responseText;
15951        
15952         var o = /* eval:var:o */ eval("("+json+")");
15953         if(!o) {
15954             throw {message: "JsonReader.read: Json object not found"};
15955         }
15956         
15957         if(o.metaData){
15958             
15959             delete this.ef;
15960             this.metaFromRemote = true;
15961             this.meta = o.metaData;
15962             this.recordType = Roo.data.Record.create(o.metaData.fields);
15963             this.onMetaChange(this.meta, this.recordType, o);
15964         }
15965         return this.readRecords(o);
15966     },
15967
15968     // private function a store will implement
15969     onMetaChange : function(meta, recordType, o){
15970
15971     },
15972
15973     /**
15974          * @ignore
15975          */
15976     simpleAccess: function(obj, subsc) {
15977         return obj[subsc];
15978     },
15979
15980         /**
15981          * @ignore
15982          */
15983     getJsonAccessor: function(){
15984         var re = /[\[\.]/;
15985         return function(expr) {
15986             try {
15987                 return(re.test(expr))
15988                     ? new Function("obj", "return obj." + expr)
15989                     : function(obj){
15990                         return obj[expr];
15991                     };
15992             } catch(e){}
15993             return Roo.emptyFn;
15994         };
15995     }(),
15996
15997     /**
15998      * Create a data block containing Roo.data.Records from an XML document.
15999      * @param {Object} o An object which contains an Array of row objects in the property specified
16000      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16001      * which contains the total size of the dataset.
16002      * @return {Object} data A data block which is used by an Roo.data.Store object as
16003      * a cache of Roo.data.Records.
16004      */
16005     readRecords : function(o){
16006         /**
16007          * After any data loads, the raw JSON data is available for further custom processing.
16008          * @type Object
16009          */
16010         this.o = o;
16011         var s = this.meta, Record = this.recordType,
16012             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16013
16014 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16015         if (!this.ef) {
16016             if(s.totalProperty) {
16017                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16018                 }
16019                 if(s.successProperty) {
16020                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16021                 }
16022                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16023                 if (s.id) {
16024                         var g = this.getJsonAccessor(s.id);
16025                         this.getId = function(rec) {
16026                                 var r = g(rec);  
16027                                 return (r === undefined || r === "") ? null : r;
16028                         };
16029                 } else {
16030                         this.getId = function(){return null;};
16031                 }
16032             this.ef = [];
16033             for(var jj = 0; jj < fl; jj++){
16034                 f = fi[jj];
16035                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16036                 this.ef[jj] = this.getJsonAccessor(map);
16037             }
16038         }
16039
16040         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16041         if(s.totalProperty){
16042             var vt = parseInt(this.getTotal(o), 10);
16043             if(!isNaN(vt)){
16044                 totalRecords = vt;
16045             }
16046         }
16047         if(s.successProperty){
16048             var vs = this.getSuccess(o);
16049             if(vs === false || vs === 'false'){
16050                 success = false;
16051             }
16052         }
16053         var records = [];
16054         for(var i = 0; i < c; i++){
16055                 var n = root[i];
16056             var values = {};
16057             var id = this.getId(n);
16058             for(var j = 0; j < fl; j++){
16059                 f = fi[j];
16060             var v = this.ef[j](n);
16061             if (!f.convert) {
16062                 Roo.log('missing convert for ' + f.name);
16063                 Roo.log(f);
16064                 continue;
16065             }
16066             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16067             }
16068             var record = new Record(values, id);
16069             record.json = n;
16070             records[i] = record;
16071         }
16072         return {
16073             raw : o,
16074             success : success,
16075             records : records,
16076             totalRecords : totalRecords
16077         };
16078     },
16079     // used when loading children.. @see loadDataFromChildren
16080     toLoadData: function(rec)
16081     {
16082         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16083         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16084         return { data : data, total : data.length };
16085         
16086     }
16087 });/*
16088  * Based on:
16089  * Ext JS Library 1.1.1
16090  * Copyright(c) 2006-2007, Ext JS, LLC.
16091  *
16092  * Originally Released Under LGPL - original licence link has changed is not relivant.
16093  *
16094  * Fork - LGPL
16095  * <script type="text/javascript">
16096  */
16097
16098 /**
16099  * @class Roo.data.ArrayReader
16100  * @extends Roo.data.DataReader
16101  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16102  * Each element of that Array represents a row of data fields. The
16103  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16104  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16105  * <p>
16106  * Example code:.
16107  * <pre><code>
16108 var RecordDef = Roo.data.Record.create([
16109     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16110     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16111 ]);
16112 var myReader = new Roo.data.ArrayReader({
16113     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16114 }, RecordDef);
16115 </code></pre>
16116  * <p>
16117  * This would consume an Array like this:
16118  * <pre><code>
16119 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16120   </code></pre>
16121  
16122  * @constructor
16123  * Create a new JsonReader
16124  * @param {Object} meta Metadata configuration options.
16125  * @param {Object|Array} recordType Either an Array of field definition objects
16126  * 
16127  * @cfg {Array} fields Array of field definition objects
16128  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16129  * as specified to {@link Roo.data.Record#create},
16130  * or an {@link Roo.data.Record} object
16131  *
16132  * 
16133  * created using {@link Roo.data.Record#create}.
16134  */
16135 Roo.data.ArrayReader = function(meta, recordType)
16136 {    
16137     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16138 };
16139
16140 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16141     
16142       /**
16143      * Create a data block containing Roo.data.Records from an XML document.
16144      * @param {Object} o An Array of row objects which represents the dataset.
16145      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16146      * a cache of Roo.data.Records.
16147      */
16148     readRecords : function(o)
16149     {
16150         var sid = this.meta ? this.meta.id : null;
16151         var recordType = this.recordType, fields = recordType.prototype.fields;
16152         var records = [];
16153         var root = o;
16154         for(var i = 0; i < root.length; i++){
16155             var n = root[i];
16156             var values = {};
16157             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16158             for(var j = 0, jlen = fields.length; j < jlen; j++){
16159                 var f = fields.items[j];
16160                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16161                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16162                 v = f.convert(v);
16163                 values[f.name] = v;
16164             }
16165             var record = new recordType(values, id);
16166             record.json = n;
16167             records[records.length] = record;
16168         }
16169         return {
16170             records : records,
16171             totalRecords : records.length
16172         };
16173     },
16174     // used when loading children.. @see loadDataFromChildren
16175     toLoadData: function(rec)
16176     {
16177         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16178         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16179         
16180     }
16181     
16182     
16183 });/*
16184  * - LGPL
16185  * * 
16186  */
16187
16188 /**
16189  * @class Roo.bootstrap.ComboBox
16190  * @extends Roo.bootstrap.TriggerField
16191  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16192  * @cfg {Boolean} append (true|false) default false
16193  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16194  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16195  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16196  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16197  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16198  * @cfg {Boolean} animate default true
16199  * @cfg {Boolean} emptyResultText only for touch device
16200  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16201  * @cfg {String} emptyTitle default ''
16202  * @cfg {Number} width fixed with? experimental
16203  * @constructor
16204  * Create a new ComboBox.
16205  * @param {Object} config Configuration options
16206  */
16207 Roo.bootstrap.ComboBox = function(config){
16208     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16209     this.addEvents({
16210         /**
16211          * @event expand
16212          * Fires when the dropdown list is expanded
16213         * @param {Roo.bootstrap.ComboBox} combo This combo box
16214         */
16215         'expand' : true,
16216         /**
16217          * @event collapse
16218          * Fires when the dropdown list is collapsed
16219         * @param {Roo.bootstrap.ComboBox} combo This combo box
16220         */
16221         'collapse' : true,
16222         /**
16223          * @event beforeselect
16224          * Fires before a list item is selected. Return false to cancel the selection.
16225         * @param {Roo.bootstrap.ComboBox} combo This combo box
16226         * @param {Roo.data.Record} record The data record returned from the underlying store
16227         * @param {Number} index The index of the selected item in the dropdown list
16228         */
16229         'beforeselect' : true,
16230         /**
16231          * @event select
16232          * Fires when a list item is selected
16233         * @param {Roo.bootstrap.ComboBox} combo This combo box
16234         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16235         * @param {Number} index The index of the selected item in the dropdown list
16236         */
16237         'select' : true,
16238         /**
16239          * @event beforequery
16240          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16241          * The event object passed has these properties:
16242         * @param {Roo.bootstrap.ComboBox} combo This combo box
16243         * @param {String} query The query
16244         * @param {Boolean} forceAll true to force "all" query
16245         * @param {Boolean} cancel true to cancel the query
16246         * @param {Object} e The query event object
16247         */
16248         'beforequery': true,
16249          /**
16250          * @event add
16251          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16252         * @param {Roo.bootstrap.ComboBox} combo This combo box
16253         */
16254         'add' : true,
16255         /**
16256          * @event edit
16257          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16258         * @param {Roo.bootstrap.ComboBox} combo This combo box
16259         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16260         */
16261         'edit' : true,
16262         /**
16263          * @event remove
16264          * Fires when the remove value from the combobox array
16265         * @param {Roo.bootstrap.ComboBox} combo This combo box
16266         */
16267         'remove' : true,
16268         /**
16269          * @event afterremove
16270          * Fires when the remove value from the combobox array
16271         * @param {Roo.bootstrap.ComboBox} combo This combo box
16272         */
16273         'afterremove' : true,
16274         /**
16275          * @event specialfilter
16276          * Fires when specialfilter
16277             * @param {Roo.bootstrap.ComboBox} combo This combo box
16278             */
16279         'specialfilter' : true,
16280         /**
16281          * @event tick
16282          * Fires when tick the element
16283             * @param {Roo.bootstrap.ComboBox} combo This combo box
16284             */
16285         'tick' : true,
16286         /**
16287          * @event touchviewdisplay
16288          * Fires when touch view require special display (default is using displayField)
16289             * @param {Roo.bootstrap.ComboBox} combo This combo box
16290             * @param {Object} cfg set html .
16291             */
16292         'touchviewdisplay' : true
16293         
16294     });
16295     
16296     this.item = [];
16297     this.tickItems = [];
16298     
16299     this.selectedIndex = -1;
16300     if(this.mode == 'local'){
16301         if(config.queryDelay === undefined){
16302             this.queryDelay = 10;
16303         }
16304         if(config.minChars === undefined){
16305             this.minChars = 0;
16306         }
16307     }
16308 };
16309
16310 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16311      
16312     /**
16313      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16314      * rendering into an Roo.Editor, defaults to false)
16315      */
16316     /**
16317      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16318      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16319      */
16320     /**
16321      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16322      */
16323     /**
16324      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16325      * the dropdown list (defaults to undefined, with no header element)
16326      */
16327
16328      /**
16329      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16330      */
16331      
16332      /**
16333      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16334      */
16335     listWidth: undefined,
16336     /**
16337      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16338      * mode = 'remote' or 'text' if mode = 'local')
16339      */
16340     displayField: undefined,
16341     
16342     /**
16343      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16344      * mode = 'remote' or 'value' if mode = 'local'). 
16345      * Note: use of a valueField requires the user make a selection
16346      * in order for a value to be mapped.
16347      */
16348     valueField: undefined,
16349     /**
16350      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16351      */
16352     modalTitle : '',
16353     
16354     /**
16355      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16356      * field's data value (defaults to the underlying DOM element's name)
16357      */
16358     hiddenName: undefined,
16359     /**
16360      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16361      */
16362     listClass: '',
16363     /**
16364      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16365      */
16366     selectedClass: 'active',
16367     
16368     /**
16369      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16370      */
16371     shadow:'sides',
16372     /**
16373      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16374      * anchor positions (defaults to 'tl-bl')
16375      */
16376     listAlign: 'tl-bl?',
16377     /**
16378      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16379      */
16380     maxHeight: 300,
16381     /**
16382      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16383      * query specified by the allQuery config option (defaults to 'query')
16384      */
16385     triggerAction: 'query',
16386     /**
16387      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16388      * (defaults to 4, does not apply if editable = false)
16389      */
16390     minChars : 4,
16391     /**
16392      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16393      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16394      */
16395     typeAhead: false,
16396     /**
16397      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16398      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16399      */
16400     queryDelay: 500,
16401     /**
16402      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16403      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16404      */
16405     pageSize: 0,
16406     /**
16407      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16408      * when editable = true (defaults to false)
16409      */
16410     selectOnFocus:false,
16411     /**
16412      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16413      */
16414     queryParam: 'query',
16415     /**
16416      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16417      * when mode = 'remote' (defaults to 'Loading...')
16418      */
16419     loadingText: 'Loading...',
16420     /**
16421      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16422      */
16423     resizable: false,
16424     /**
16425      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16426      */
16427     handleHeight : 8,
16428     /**
16429      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16430      * traditional select (defaults to true)
16431      */
16432     editable: true,
16433     /**
16434      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16435      */
16436     allQuery: '',
16437     /**
16438      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16439      */
16440     mode: 'remote',
16441     /**
16442      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16443      * listWidth has a higher value)
16444      */
16445     minListWidth : 70,
16446     /**
16447      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16448      * allow the user to set arbitrary text into the field (defaults to false)
16449      */
16450     forceSelection:false,
16451     /**
16452      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16453      * if typeAhead = true (defaults to 250)
16454      */
16455     typeAheadDelay : 250,
16456     /**
16457      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16458      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16459      */
16460     valueNotFoundText : undefined,
16461     /**
16462      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16463      */
16464     blockFocus : false,
16465     
16466     /**
16467      * @cfg {Boolean} disableClear Disable showing of clear button.
16468      */
16469     disableClear : false,
16470     /**
16471      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16472      */
16473     alwaysQuery : false,
16474     
16475     /**
16476      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16477      */
16478     multiple : false,
16479     
16480     /**
16481      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16482      */
16483     invalidClass : "has-warning",
16484     
16485     /**
16486      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16487      */
16488     validClass : "has-success",
16489     
16490     /**
16491      * @cfg {Boolean} specialFilter (true|false) special filter default false
16492      */
16493     specialFilter : false,
16494     
16495     /**
16496      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16497      */
16498     mobileTouchView : true,
16499     
16500     /**
16501      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16502      */
16503     useNativeIOS : false,
16504     
16505     /**
16506      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16507      */
16508     mobile_restrict_height : false,
16509     
16510     ios_options : false,
16511     
16512     //private
16513     addicon : false,
16514     editicon: false,
16515     
16516     page: 0,
16517     hasQuery: false,
16518     append: false,
16519     loadNext: false,
16520     autoFocus : true,
16521     tickable : false,
16522     btnPosition : 'right',
16523     triggerList : true,
16524     showToggleBtn : true,
16525     animate : true,
16526     emptyResultText: 'Empty',
16527     triggerText : 'Select',
16528     emptyTitle : '',
16529     width : false,
16530     
16531     // element that contains real text value.. (when hidden is used..)
16532     
16533     getAutoCreate : function()
16534     {   
16535         var cfg = false;
16536         //render
16537         /*
16538          * Render classic select for iso
16539          */
16540         
16541         if(Roo.isIOS && this.useNativeIOS){
16542             cfg = this.getAutoCreateNativeIOS();
16543             return cfg;
16544         }
16545         
16546         /*
16547          * Touch Devices
16548          */
16549         
16550         if(Roo.isTouch && this.mobileTouchView){
16551             cfg = this.getAutoCreateTouchView();
16552             return cfg;;
16553         }
16554         
16555         /*
16556          *  Normal ComboBox
16557          */
16558         if(!this.tickable){
16559             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16560             return cfg;
16561         }
16562         
16563         /*
16564          *  ComboBox with tickable selections
16565          */
16566              
16567         var align = this.labelAlign || this.parentLabelAlign();
16568         
16569         cfg = {
16570             cls : 'form-group roo-combobox-tickable' //input-group
16571         };
16572         
16573         var btn_text_select = '';
16574         var btn_text_done = '';
16575         var btn_text_cancel = '';
16576         
16577         if (this.btn_text_show) {
16578             btn_text_select = 'Select';
16579             btn_text_done = 'Done';
16580             btn_text_cancel = 'Cancel'; 
16581         }
16582         
16583         var buttons = {
16584             tag : 'div',
16585             cls : 'tickable-buttons',
16586             cn : [
16587                 {
16588                     tag : 'button',
16589                     type : 'button',
16590                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16591                     //html : this.triggerText
16592                     html: btn_text_select
16593                 },
16594                 {
16595                     tag : 'button',
16596                     type : 'button',
16597                     name : 'ok',
16598                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16599                     //html : 'Done'
16600                     html: btn_text_done
16601                 },
16602                 {
16603                     tag : 'button',
16604                     type : 'button',
16605                     name : 'cancel',
16606                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16607                     //html : 'Cancel'
16608                     html: btn_text_cancel
16609                 }
16610             ]
16611         };
16612         
16613         if(this.editable){
16614             buttons.cn.unshift({
16615                 tag: 'input',
16616                 cls: 'roo-select2-search-field-input'
16617             });
16618         }
16619         
16620         var _this = this;
16621         
16622         Roo.each(buttons.cn, function(c){
16623             if (_this.size) {
16624                 c.cls += ' btn-' + _this.size;
16625             }
16626
16627             if (_this.disabled) {
16628                 c.disabled = true;
16629             }
16630         });
16631         
16632         var box = {
16633             tag: 'div',
16634             style : 'display: contents',
16635             cn: [
16636                 {
16637                     tag: 'input',
16638                     type : 'hidden',
16639                     cls: 'form-hidden-field'
16640                 },
16641                 {
16642                     tag: 'ul',
16643                     cls: 'roo-select2-choices',
16644                     cn:[
16645                         {
16646                             tag: 'li',
16647                             cls: 'roo-select2-search-field',
16648                             cn: [
16649                                 buttons
16650                             ]
16651                         }
16652                     ]
16653                 }
16654             ]
16655         };
16656         
16657         var combobox = {
16658             cls: 'roo-select2-container input-group roo-select2-container-multi',
16659             cn: [
16660                 
16661                 box
16662 //                {
16663 //                    tag: 'ul',
16664 //                    cls: 'typeahead typeahead-long dropdown-menu',
16665 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16666 //                }
16667             ]
16668         };
16669         
16670         if(this.hasFeedback && !this.allowBlank){
16671             
16672             var feedback = {
16673                 tag: 'span',
16674                 cls: 'glyphicon form-control-feedback'
16675             };
16676
16677             combobox.cn.push(feedback);
16678         }
16679         
16680         
16681         
16682         var indicator = {
16683             tag : 'i',
16684             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16685             tooltip : 'This field is required'
16686         };
16687         if (Roo.bootstrap.version == 4) {
16688             indicator = {
16689                 tag : 'i',
16690                 style : 'display:none'
16691             };
16692         }
16693         if (align ==='left' && this.fieldLabel.length) {
16694             
16695             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16696             
16697             cfg.cn = [
16698                 indicator,
16699                 {
16700                     tag: 'label',
16701                     'for' :  id,
16702                     cls : 'control-label col-form-label',
16703                     html : this.fieldLabel
16704
16705                 },
16706                 {
16707                     cls : "", 
16708                     cn: [
16709                         combobox
16710                     ]
16711                 }
16712
16713             ];
16714             
16715             var labelCfg = cfg.cn[1];
16716             var contentCfg = cfg.cn[2];
16717             
16718
16719             if(this.indicatorpos == 'right'){
16720                 
16721                 cfg.cn = [
16722                     {
16723                         tag: 'label',
16724                         'for' :  id,
16725                         cls : 'control-label col-form-label',
16726                         cn : [
16727                             {
16728                                 tag : 'span',
16729                                 html : this.fieldLabel
16730                             },
16731                             indicator
16732                         ]
16733                     },
16734                     {
16735                         cls : "",
16736                         cn: [
16737                             combobox
16738                         ]
16739                     }
16740
16741                 ];
16742                 
16743                 
16744                 
16745                 labelCfg = cfg.cn[0];
16746                 contentCfg = cfg.cn[1];
16747             
16748             }
16749             
16750             if(this.labelWidth > 12){
16751                 labelCfg.style = "width: " + this.labelWidth + 'px';
16752             }
16753             if(this.width * 1 > 0){
16754                 contentCfg.style = "width: " + this.width + 'px';
16755             }
16756             if(this.labelWidth < 13 && this.labelmd == 0){
16757                 this.labelmd = this.labelWidth;
16758             }
16759             
16760             if(this.labellg > 0){
16761                 labelCfg.cls += ' col-lg-' + this.labellg;
16762                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16763             }
16764             
16765             if(this.labelmd > 0){
16766                 labelCfg.cls += ' col-md-' + this.labelmd;
16767                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16768             }
16769             
16770             if(this.labelsm > 0){
16771                 labelCfg.cls += ' col-sm-' + this.labelsm;
16772                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16773             }
16774             
16775             if(this.labelxs > 0){
16776                 labelCfg.cls += ' col-xs-' + this.labelxs;
16777                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16778             }
16779                 
16780                 
16781         } else if ( this.fieldLabel.length) {
16782 //                Roo.log(" label");
16783                  cfg.cn = [
16784                    indicator,
16785                     {
16786                         tag: 'label',
16787                         //cls : 'input-group-addon',
16788                         html : this.fieldLabel
16789                     },
16790                     combobox
16791                 ];
16792                 
16793                 if(this.indicatorpos == 'right'){
16794                     cfg.cn = [
16795                         {
16796                             tag: 'label',
16797                             //cls : 'input-group-addon',
16798                             html : this.fieldLabel
16799                         },
16800                         indicator,
16801                         combobox
16802                     ];
16803                     
16804                 }
16805
16806         } else {
16807             
16808 //                Roo.log(" no label && no align");
16809                 cfg = combobox
16810                      
16811                 
16812         }
16813          
16814         var settings=this;
16815         ['xs','sm','md','lg'].map(function(size){
16816             if (settings[size]) {
16817                 cfg.cls += ' col-' + size + '-' + settings[size];
16818             }
16819         });
16820         
16821         return cfg;
16822         
16823     },
16824     
16825     _initEventsCalled : false,
16826     
16827     // private
16828     initEvents: function()
16829     {   
16830         if (this._initEventsCalled) { // as we call render... prevent looping...
16831             return;
16832         }
16833         this._initEventsCalled = true;
16834         
16835         if (!this.store) {
16836             throw "can not find store for combo";
16837         }
16838         
16839         this.indicator = this.indicatorEl();
16840         
16841         this.store = Roo.factory(this.store, Roo.data);
16842         this.store.parent = this;
16843         
16844         // if we are building from html. then this element is so complex, that we can not really
16845         // use the rendered HTML.
16846         // so we have to trash and replace the previous code.
16847         if (Roo.XComponent.build_from_html) {
16848             // remove this element....
16849             var e = this.el.dom, k=0;
16850             while (e ) { e = e.previousSibling;  ++k;}
16851
16852             this.el.remove();
16853             
16854             this.el=false;
16855             this.rendered = false;
16856             
16857             this.render(this.parent().getChildContainer(true), k);
16858         }
16859         
16860         if(Roo.isIOS && this.useNativeIOS){
16861             this.initIOSView();
16862             return;
16863         }
16864         
16865         /*
16866          * Touch Devices
16867          */
16868         
16869         if(Roo.isTouch && this.mobileTouchView){
16870             this.initTouchView();
16871             return;
16872         }
16873         
16874         if(this.tickable){
16875             this.initTickableEvents();
16876             return;
16877         }
16878         
16879         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16880         
16881         if(this.hiddenName){
16882             
16883             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16884             
16885             this.hiddenField.dom.value =
16886                 this.hiddenValue !== undefined ? this.hiddenValue :
16887                 this.value !== undefined ? this.value : '';
16888
16889             // prevent input submission
16890             this.el.dom.removeAttribute('name');
16891             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16892              
16893              
16894         }
16895         //if(Roo.isGecko){
16896         //    this.el.dom.setAttribute('autocomplete', 'off');
16897         //}
16898         
16899         var cls = 'x-combo-list';
16900         
16901         //this.list = new Roo.Layer({
16902         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16903         //});
16904         
16905         var _this = this;
16906         
16907         (function(){
16908             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16909             _this.list.setWidth(lw);
16910         }).defer(100);
16911         
16912         this.list.on('mouseover', this.onViewOver, this);
16913         this.list.on('mousemove', this.onViewMove, this);
16914         this.list.on('scroll', this.onViewScroll, this);
16915         
16916         /*
16917         this.list.swallowEvent('mousewheel');
16918         this.assetHeight = 0;
16919
16920         if(this.title){
16921             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16922             this.assetHeight += this.header.getHeight();
16923         }
16924
16925         this.innerList = this.list.createChild({cls:cls+'-inner'});
16926         this.innerList.on('mouseover', this.onViewOver, this);
16927         this.innerList.on('mousemove', this.onViewMove, this);
16928         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16929         
16930         if(this.allowBlank && !this.pageSize && !this.disableClear){
16931             this.footer = this.list.createChild({cls:cls+'-ft'});
16932             this.pageTb = new Roo.Toolbar(this.footer);
16933            
16934         }
16935         if(this.pageSize){
16936             this.footer = this.list.createChild({cls:cls+'-ft'});
16937             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16938                     {pageSize: this.pageSize});
16939             
16940         }
16941         
16942         if (this.pageTb && this.allowBlank && !this.disableClear) {
16943             var _this = this;
16944             this.pageTb.add(new Roo.Toolbar.Fill(), {
16945                 cls: 'x-btn-icon x-btn-clear',
16946                 text: '&#160;',
16947                 handler: function()
16948                 {
16949                     _this.collapse();
16950                     _this.clearValue();
16951                     _this.onSelect(false, -1);
16952                 }
16953             });
16954         }
16955         if (this.footer) {
16956             this.assetHeight += this.footer.getHeight();
16957         }
16958         */
16959             
16960         if(!this.tpl){
16961             this.tpl = Roo.bootstrap.version == 4 ?
16962                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16963                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16964         }
16965
16966         this.view = new Roo.View(this.list, this.tpl, {
16967             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16968         });
16969         //this.view.wrapEl.setDisplayed(false);
16970         this.view.on('click', this.onViewClick, this);
16971         
16972         
16973         this.store.on('beforeload', this.onBeforeLoad, this);
16974         this.store.on('load', this.onLoad, this);
16975         this.store.on('loadexception', this.onLoadException, this);
16976         /*
16977         if(this.resizable){
16978             this.resizer = new Roo.Resizable(this.list,  {
16979                pinned:true, handles:'se'
16980             });
16981             this.resizer.on('resize', function(r, w, h){
16982                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16983                 this.listWidth = w;
16984                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16985                 this.restrictHeight();
16986             }, this);
16987             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16988         }
16989         */
16990         if(!this.editable){
16991             this.editable = true;
16992             this.setEditable(false);
16993         }
16994         
16995         /*
16996         
16997         if (typeof(this.events.add.listeners) != 'undefined') {
16998             
16999             this.addicon = this.wrap.createChild(
17000                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17001        
17002             this.addicon.on('click', function(e) {
17003                 this.fireEvent('add', this);
17004             }, this);
17005         }
17006         if (typeof(this.events.edit.listeners) != 'undefined') {
17007             
17008             this.editicon = this.wrap.createChild(
17009                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17010             if (this.addicon) {
17011                 this.editicon.setStyle('margin-left', '40px');
17012             }
17013             this.editicon.on('click', function(e) {
17014                 
17015                 // we fire even  if inothing is selected..
17016                 this.fireEvent('edit', this, this.lastData );
17017                 
17018             }, this);
17019         }
17020         */
17021         
17022         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17023             "up" : function(e){
17024                 this.inKeyMode = true;
17025                 this.selectPrev();
17026             },
17027
17028             "down" : function(e){
17029                 if(!this.isExpanded()){
17030                     this.onTriggerClick();
17031                 }else{
17032                     this.inKeyMode = true;
17033                     this.selectNext();
17034                 }
17035             },
17036
17037             "enter" : function(e){
17038 //                this.onViewClick();
17039                 //return true;
17040                 this.collapse();
17041                 
17042                 if(this.fireEvent("specialkey", this, e)){
17043                     this.onViewClick(false);
17044                 }
17045                 
17046                 return true;
17047             },
17048
17049             "esc" : function(e){
17050                 this.collapse();
17051             },
17052
17053             "tab" : function(e){
17054                 this.collapse();
17055                 
17056                 if(this.fireEvent("specialkey", this, e)){
17057                     this.onViewClick(false);
17058                 }
17059                 
17060                 return true;
17061             },
17062
17063             scope : this,
17064
17065             doRelay : function(foo, bar, hname){
17066                 if(hname == 'down' || this.scope.isExpanded()){
17067                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17068                 }
17069                 return true;
17070             },
17071
17072             forceKeyDown: true
17073         });
17074         
17075         
17076         this.queryDelay = Math.max(this.queryDelay || 10,
17077                 this.mode == 'local' ? 10 : 250);
17078         
17079         
17080         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17081         
17082         if(this.typeAhead){
17083             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17084         }
17085         if(this.editable !== false){
17086             this.inputEl().on("keyup", this.onKeyUp, this);
17087         }
17088         if(this.forceSelection){
17089             this.inputEl().on('blur', this.doForce, this);
17090         }
17091         
17092         if(this.multiple){
17093             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17094             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17095         }
17096     },
17097     
17098     initTickableEvents: function()
17099     {   
17100         this.createList();
17101         
17102         if(this.hiddenName){
17103             
17104             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17105             
17106             this.hiddenField.dom.value =
17107                 this.hiddenValue !== undefined ? this.hiddenValue :
17108                 this.value !== undefined ? this.value : '';
17109
17110             // prevent input submission
17111             this.el.dom.removeAttribute('name');
17112             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17113              
17114              
17115         }
17116         
17117 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17118         
17119         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17120         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17121         if(this.triggerList){
17122             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17123         }
17124          
17125         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17126         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17127         
17128         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17129         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17130         
17131         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17132         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17133         
17134         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17135         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17136         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17137         
17138         this.okBtn.hide();
17139         this.cancelBtn.hide();
17140         
17141         var _this = this;
17142         
17143         (function(){
17144             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17145             _this.list.setWidth(lw);
17146         }).defer(100);
17147         
17148         this.list.on('mouseover', this.onViewOver, this);
17149         this.list.on('mousemove', this.onViewMove, this);
17150         
17151         this.list.on('scroll', this.onViewScroll, this);
17152         
17153         if(!this.tpl){
17154             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17155                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17156         }
17157
17158         this.view = new Roo.View(this.list, this.tpl, {
17159             singleSelect:true,
17160             tickable:true,
17161             parent:this,
17162             store: this.store,
17163             selectedClass: this.selectedClass
17164         });
17165         
17166         //this.view.wrapEl.setDisplayed(false);
17167         this.view.on('click', this.onViewClick, this);
17168         
17169         
17170         
17171         this.store.on('beforeload', this.onBeforeLoad, this);
17172         this.store.on('load', this.onLoad, this);
17173         this.store.on('loadexception', this.onLoadException, this);
17174         
17175         if(this.editable){
17176             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17177                 "up" : function(e){
17178                     this.inKeyMode = true;
17179                     this.selectPrev();
17180                 },
17181
17182                 "down" : function(e){
17183                     this.inKeyMode = true;
17184                     this.selectNext();
17185                 },
17186
17187                 "enter" : function(e){
17188                     if(this.fireEvent("specialkey", this, e)){
17189                         this.onViewClick(false);
17190                     }
17191                     
17192                     return true;
17193                 },
17194
17195                 "esc" : function(e){
17196                     this.onTickableFooterButtonClick(e, false, false);
17197                 },
17198
17199                 "tab" : function(e){
17200                     this.fireEvent("specialkey", this, e);
17201                     
17202                     this.onTickableFooterButtonClick(e, false, false);
17203                     
17204                     return true;
17205                 },
17206
17207                 scope : this,
17208
17209                 doRelay : function(e, fn, key){
17210                     if(this.scope.isExpanded()){
17211                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17212                     }
17213                     return true;
17214                 },
17215
17216                 forceKeyDown: true
17217             });
17218         }
17219         
17220         this.queryDelay = Math.max(this.queryDelay || 10,
17221                 this.mode == 'local' ? 10 : 250);
17222         
17223         
17224         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17225         
17226         if(this.typeAhead){
17227             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17228         }
17229         
17230         if(this.editable !== false){
17231             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17232         }
17233         
17234         this.indicator = this.indicatorEl();
17235         
17236         if(this.indicator){
17237             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17238             this.indicator.hide();
17239         }
17240         
17241     },
17242
17243     onDestroy : function(){
17244         if(this.view){
17245             this.view.setStore(null);
17246             this.view.el.removeAllListeners();
17247             this.view.el.remove();
17248             this.view.purgeListeners();
17249         }
17250         if(this.list){
17251             this.list.dom.innerHTML  = '';
17252         }
17253         
17254         if(this.store){
17255             this.store.un('beforeload', this.onBeforeLoad, this);
17256             this.store.un('load', this.onLoad, this);
17257             this.store.un('loadexception', this.onLoadException, this);
17258         }
17259         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17260     },
17261
17262     // private
17263     fireKey : function(e){
17264         if(e.isNavKeyPress() && !this.list.isVisible()){
17265             this.fireEvent("specialkey", this, e);
17266         }
17267     },
17268
17269     // private
17270     onResize: function(w, h)
17271     {
17272         
17273         
17274 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17275 //        
17276 //        if(typeof w != 'number'){
17277 //            // we do not handle it!?!?
17278 //            return;
17279 //        }
17280 //        var tw = this.trigger.getWidth();
17281 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17282 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17283 //        var x = w - tw;
17284 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17285 //            
17286 //        //this.trigger.setStyle('left', x+'px');
17287 //        
17288 //        if(this.list && this.listWidth === undefined){
17289 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17290 //            this.list.setWidth(lw);
17291 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17292 //        }
17293         
17294     
17295         
17296     },
17297
17298     /**
17299      * Allow or prevent the user from directly editing the field text.  If false is passed,
17300      * the user will only be able to select from the items defined in the dropdown list.  This method
17301      * is the runtime equivalent of setting the 'editable' config option at config time.
17302      * @param {Boolean} value True to allow the user to directly edit the field text
17303      */
17304     setEditable : function(value){
17305         if(value == this.editable){
17306             return;
17307         }
17308         this.editable = value;
17309         if(!value){
17310             this.inputEl().dom.setAttribute('readOnly', true);
17311             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17312             this.inputEl().addClass('x-combo-noedit');
17313         }else{
17314             this.inputEl().dom.removeAttribute('readOnly');
17315             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17316             this.inputEl().removeClass('x-combo-noedit');
17317         }
17318     },
17319
17320     // private
17321     
17322     onBeforeLoad : function(combo,opts){
17323         if(!this.hasFocus){
17324             return;
17325         }
17326          if (!opts.add) {
17327             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17328          }
17329         this.restrictHeight();
17330         this.selectedIndex = -1;
17331     },
17332
17333     // private
17334     onLoad : function(){
17335         
17336         this.hasQuery = false;
17337         
17338         if(!this.hasFocus){
17339             return;
17340         }
17341         
17342         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17343             this.loading.hide();
17344         }
17345         
17346         if(this.store.getCount() > 0){
17347             
17348             this.expand();
17349             this.restrictHeight();
17350             if(this.lastQuery == this.allQuery){
17351                 if(this.editable && !this.tickable){
17352                     this.inputEl().dom.select();
17353                 }
17354                 
17355                 if(
17356                     !this.selectByValue(this.value, true) &&
17357                     this.autoFocus && 
17358                     (
17359                         !this.store.lastOptions ||
17360                         typeof(this.store.lastOptions.add) == 'undefined' || 
17361                         this.store.lastOptions.add != true
17362                     )
17363                 ){
17364                     this.select(0, true);
17365                 }
17366             }else{
17367                 if(this.autoFocus){
17368                     this.selectNext();
17369                 }
17370                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17371                     this.taTask.delay(this.typeAheadDelay);
17372                 }
17373             }
17374         }else{
17375             this.onEmptyResults();
17376         }
17377         
17378         //this.el.focus();
17379     },
17380     // private
17381     onLoadException : function()
17382     {
17383         this.hasQuery = false;
17384         
17385         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17386             this.loading.hide();
17387         }
17388         
17389         if(this.tickable && this.editable){
17390             return;
17391         }
17392         
17393         this.collapse();
17394         // only causes errors at present
17395         //Roo.log(this.store.reader.jsonData);
17396         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17397             // fixme
17398             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17399         //}
17400         
17401         
17402     },
17403     // private
17404     onTypeAhead : function(){
17405         if(this.store.getCount() > 0){
17406             var r = this.store.getAt(0);
17407             var newValue = r.data[this.displayField];
17408             var len = newValue.length;
17409             var selStart = this.getRawValue().length;
17410             
17411             if(selStart != len){
17412                 this.setRawValue(newValue);
17413                 this.selectText(selStart, newValue.length);
17414             }
17415         }
17416     },
17417
17418     // private
17419     onSelect : function(record, index){
17420         
17421         if(this.fireEvent('beforeselect', this, record, index) !== false){
17422         
17423             this.setFromData(index > -1 ? record.data : false);
17424             
17425             this.collapse();
17426             this.fireEvent('select', this, record, index);
17427         }
17428     },
17429
17430     /**
17431      * Returns the currently selected field value or empty string if no value is set.
17432      * @return {String} value The selected value
17433      */
17434     getValue : function()
17435     {
17436         if(Roo.isIOS && this.useNativeIOS){
17437             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17438         }
17439         
17440         if(this.multiple){
17441             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17442         }
17443         
17444         if(this.valueField){
17445             return typeof this.value != 'undefined' ? this.value : '';
17446         }else{
17447             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17448         }
17449     },
17450     
17451     getRawValue : function()
17452     {
17453         if(Roo.isIOS && this.useNativeIOS){
17454             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17455         }
17456         
17457         var v = this.inputEl().getValue();
17458         
17459         return v;
17460     },
17461
17462     /**
17463      * Clears any text/value currently set in the field
17464      */
17465     clearValue : function(){
17466         
17467         if(this.hiddenField){
17468             this.hiddenField.dom.value = '';
17469         }
17470         this.value = '';
17471         this.setRawValue('');
17472         this.lastSelectionText = '';
17473         this.lastData = false;
17474         
17475         var close = this.closeTriggerEl();
17476         
17477         if(close){
17478             close.hide();
17479         }
17480         
17481         this.validate();
17482         
17483     },
17484
17485     /**
17486      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17487      * will be displayed in the field.  If the value does not match the data value of an existing item,
17488      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17489      * Otherwise the field will be blank (although the value will still be set).
17490      * @param {String} value The value to match
17491      */
17492     setValue : function(v)
17493     {
17494         if(Roo.isIOS && this.useNativeIOS){
17495             this.setIOSValue(v);
17496             return;
17497         }
17498         
17499         if(this.multiple){
17500             this.syncValue();
17501             return;
17502         }
17503         
17504         var text = v;
17505         if(this.valueField){
17506             var r = this.findRecord(this.valueField, v);
17507             if(r){
17508                 text = r.data[this.displayField];
17509             }else if(this.valueNotFoundText !== undefined){
17510                 text = this.valueNotFoundText;
17511             }
17512         }
17513         this.lastSelectionText = text;
17514         if(this.hiddenField){
17515             this.hiddenField.dom.value = v;
17516         }
17517         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17518         this.value = v;
17519         
17520         var close = this.closeTriggerEl();
17521         
17522         if(close){
17523             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17524         }
17525         
17526         this.validate();
17527     },
17528     /**
17529      * @property {Object} the last set data for the element
17530      */
17531     
17532     lastData : false,
17533     /**
17534      * Sets the value of the field based on a object which is related to the record format for the store.
17535      * @param {Object} value the value to set as. or false on reset?
17536      */
17537     setFromData : function(o){
17538         
17539         if(this.multiple){
17540             this.addItem(o);
17541             return;
17542         }
17543             
17544         var dv = ''; // display value
17545         var vv = ''; // value value..
17546         this.lastData = o;
17547         if (this.displayField) {
17548             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17549         } else {
17550             // this is an error condition!!!
17551             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17552         }
17553         
17554         if(this.valueField){
17555             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17556         }
17557         
17558         var close = this.closeTriggerEl();
17559         
17560         if(close){
17561             if(dv.length || vv * 1 > 0){
17562                 close.show() ;
17563                 this.blockFocus=true;
17564             } else {
17565                 close.hide();
17566             }             
17567         }
17568         
17569         if(this.hiddenField){
17570             this.hiddenField.dom.value = vv;
17571             
17572             this.lastSelectionText = dv;
17573             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17574             this.value = vv;
17575             return;
17576         }
17577         // no hidden field.. - we store the value in 'value', but still display
17578         // display field!!!!
17579         this.lastSelectionText = dv;
17580         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17581         this.value = vv;
17582         
17583         
17584         
17585     },
17586     // private
17587     reset : function(){
17588         // overridden so that last data is reset..
17589         
17590         if(this.multiple){
17591             this.clearItem();
17592             return;
17593         }
17594         
17595         this.setValue(this.originalValue);
17596         //this.clearInvalid();
17597         this.lastData = false;
17598         if (this.view) {
17599             this.view.clearSelections();
17600         }
17601         
17602         this.validate();
17603     },
17604     // private
17605     findRecord : function(prop, value){
17606         var record;
17607         if(this.store.getCount() > 0){
17608             this.store.each(function(r){
17609                 if(r.data[prop] == value){
17610                     record = r;
17611                     return false;
17612                 }
17613                 return true;
17614             });
17615         }
17616         return record;
17617     },
17618     
17619     getName: function()
17620     {
17621         // returns hidden if it's set..
17622         if (!this.rendered) {return ''};
17623         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17624         
17625     },
17626     // private
17627     onViewMove : function(e, t){
17628         this.inKeyMode = false;
17629     },
17630
17631     // private
17632     onViewOver : function(e, t){
17633         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17634             return;
17635         }
17636         var item = this.view.findItemFromChild(t);
17637         
17638         if(item){
17639             var index = this.view.indexOf(item);
17640             this.select(index, false);
17641         }
17642     },
17643
17644     // private
17645     onViewClick : function(view, doFocus, el, e)
17646     {
17647         var index = this.view.getSelectedIndexes()[0];
17648         
17649         var r = this.store.getAt(index);
17650         
17651         if(this.tickable){
17652             
17653             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17654                 return;
17655             }
17656             
17657             var rm = false;
17658             var _this = this;
17659             
17660             Roo.each(this.tickItems, function(v,k){
17661                 
17662                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17663                     Roo.log(v);
17664                     _this.tickItems.splice(k, 1);
17665                     
17666                     if(typeof(e) == 'undefined' && view == false){
17667                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17668                     }
17669                     
17670                     rm = true;
17671                     return;
17672                 }
17673             });
17674             
17675             if(rm){
17676                 return;
17677             }
17678             
17679             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17680                 this.tickItems.push(r.data);
17681             }
17682             
17683             if(typeof(e) == 'undefined' && view == false){
17684                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17685             }
17686                     
17687             return;
17688         }
17689         
17690         if(r){
17691             this.onSelect(r, index);
17692         }
17693         if(doFocus !== false && !this.blockFocus){
17694             this.inputEl().focus();
17695         }
17696     },
17697
17698     // private
17699     restrictHeight : function(){
17700         //this.innerList.dom.style.height = '';
17701         //var inner = this.innerList.dom;
17702         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17703         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17704         //this.list.beginUpdate();
17705         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17706         this.list.alignTo(this.inputEl(), this.listAlign);
17707         this.list.alignTo(this.inputEl(), this.listAlign);
17708         //this.list.endUpdate();
17709     },
17710
17711     // private
17712     onEmptyResults : function(){
17713         
17714         if(this.tickable && this.editable){
17715             this.hasFocus = false;
17716             this.restrictHeight();
17717             return;
17718         }
17719         
17720         this.collapse();
17721     },
17722
17723     /**
17724      * Returns true if the dropdown list is expanded, else false.
17725      */
17726     isExpanded : function(){
17727         return this.list.isVisible();
17728     },
17729
17730     /**
17731      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17732      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17733      * @param {String} value The data value of the item to select
17734      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17735      * selected item if it is not currently in view (defaults to true)
17736      * @return {Boolean} True if the value matched an item in the list, else false
17737      */
17738     selectByValue : function(v, scrollIntoView){
17739         if(v !== undefined && v !== null){
17740             var r = this.findRecord(this.valueField || this.displayField, v);
17741             if(r){
17742                 this.select(this.store.indexOf(r), scrollIntoView);
17743                 return true;
17744             }
17745         }
17746         return false;
17747     },
17748
17749     /**
17750      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17751      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17752      * @param {Number} index The zero-based index of the list item to select
17753      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17754      * selected item if it is not currently in view (defaults to true)
17755      */
17756     select : function(index, scrollIntoView){
17757         this.selectedIndex = index;
17758         this.view.select(index);
17759         if(scrollIntoView !== false){
17760             var el = this.view.getNode(index);
17761             /*
17762              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17763              */
17764             if(el){
17765                 this.list.scrollChildIntoView(el, false);
17766             }
17767         }
17768     },
17769
17770     // private
17771     selectNext : function(){
17772         var ct = this.store.getCount();
17773         if(ct > 0){
17774             if(this.selectedIndex == -1){
17775                 this.select(0);
17776             }else if(this.selectedIndex < ct-1){
17777                 this.select(this.selectedIndex+1);
17778             }
17779         }
17780     },
17781
17782     // private
17783     selectPrev : function(){
17784         var ct = this.store.getCount();
17785         if(ct > 0){
17786             if(this.selectedIndex == -1){
17787                 this.select(0);
17788             }else if(this.selectedIndex != 0){
17789                 this.select(this.selectedIndex-1);
17790             }
17791         }
17792     },
17793
17794     // private
17795     onKeyUp : function(e){
17796         if(this.editable !== false && !e.isSpecialKey()){
17797             this.lastKey = e.getKey();
17798             this.dqTask.delay(this.queryDelay);
17799         }
17800     },
17801
17802     // private
17803     validateBlur : function(){
17804         return !this.list || !this.list.isVisible();   
17805     },
17806
17807     // private
17808     initQuery : function(){
17809         
17810         var v = this.getRawValue();
17811         
17812         if(this.tickable && this.editable){
17813             v = this.tickableInputEl().getValue();
17814         }
17815         
17816         this.doQuery(v);
17817     },
17818
17819     // private
17820     doForce : function(){
17821         if(this.inputEl().dom.value.length > 0){
17822             this.inputEl().dom.value =
17823                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17824              
17825         }
17826     },
17827
17828     /**
17829      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17830      * query allowing the query action to be canceled if needed.
17831      * @param {String} query The SQL query to execute
17832      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17833      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17834      * saved in the current store (defaults to false)
17835      */
17836     doQuery : function(q, forceAll){
17837         
17838         if(q === undefined || q === null){
17839             q = '';
17840         }
17841         var qe = {
17842             query: q,
17843             forceAll: forceAll,
17844             combo: this,
17845             cancel:false
17846         };
17847         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17848             return false;
17849         }
17850         q = qe.query;
17851         
17852         forceAll = qe.forceAll;
17853         if(forceAll === true || (q.length >= this.minChars)){
17854             
17855             this.hasQuery = true;
17856             
17857             if(this.lastQuery != q || this.alwaysQuery){
17858                 this.lastQuery = q;
17859                 if(this.mode == 'local'){
17860                     this.selectedIndex = -1;
17861                     if(forceAll){
17862                         this.store.clearFilter();
17863                     }else{
17864                         
17865                         if(this.specialFilter){
17866                             this.fireEvent('specialfilter', this);
17867                             this.onLoad();
17868                             return;
17869                         }
17870                         
17871                         this.store.filter(this.displayField, q);
17872                     }
17873                     
17874                     this.store.fireEvent("datachanged", this.store);
17875                     
17876                     this.onLoad();
17877                     
17878                     
17879                 }else{
17880                     
17881                     this.store.baseParams[this.queryParam] = q;
17882                     
17883                     var options = {params : this.getParams(q)};
17884                     
17885                     if(this.loadNext){
17886                         options.add = true;
17887                         options.params.start = this.page * this.pageSize;
17888                     }
17889                     
17890                     this.store.load(options);
17891                     
17892                     /*
17893                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17894                      *  we should expand the list on onLoad
17895                      *  so command out it
17896                      */
17897 //                    this.expand();
17898                 }
17899             }else{
17900                 this.selectedIndex = -1;
17901                 this.onLoad();   
17902             }
17903         }
17904         
17905         this.loadNext = false;
17906     },
17907     
17908     // private
17909     getParams : function(q){
17910         var p = {};
17911         //p[this.queryParam] = q;
17912         
17913         if(this.pageSize){
17914             p.start = 0;
17915             p.limit = this.pageSize;
17916         }
17917         return p;
17918     },
17919
17920     /**
17921      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17922      */
17923     collapse : function(){
17924         if(!this.isExpanded()){
17925             return;
17926         }
17927         
17928         this.list.hide();
17929         
17930         this.hasFocus = false;
17931         
17932         if(this.tickable){
17933             this.okBtn.hide();
17934             this.cancelBtn.hide();
17935             this.trigger.show();
17936             
17937             if(this.editable){
17938                 this.tickableInputEl().dom.value = '';
17939                 this.tickableInputEl().blur();
17940             }
17941             
17942         }
17943         
17944         Roo.get(document).un('mousedown', this.collapseIf, this);
17945         Roo.get(document).un('mousewheel', this.collapseIf, this);
17946         if (!this.editable) {
17947             Roo.get(document).un('keydown', this.listKeyPress, this);
17948         }
17949         this.fireEvent('collapse', this);
17950         
17951         this.validate();
17952     },
17953
17954     // private
17955     collapseIf : function(e){
17956         var in_combo  = e.within(this.el);
17957         var in_list =  e.within(this.list);
17958         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17959         
17960         if (in_combo || in_list || is_list) {
17961             //e.stopPropagation();
17962             return;
17963         }
17964         
17965         if(this.tickable){
17966             this.onTickableFooterButtonClick(e, false, false);
17967         }
17968
17969         this.collapse();
17970         
17971     },
17972
17973     /**
17974      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17975      */
17976     expand : function(){
17977        
17978         if(this.isExpanded() || !this.hasFocus){
17979             return;
17980         }
17981         
17982         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17983         this.list.setWidth(lw);
17984         
17985         Roo.log('expand');
17986         
17987         this.list.show();
17988         
17989         this.restrictHeight();
17990         
17991         if(this.tickable){
17992             
17993             this.tickItems = Roo.apply([], this.item);
17994             
17995             this.okBtn.show();
17996             this.cancelBtn.show();
17997             this.trigger.hide();
17998             
17999             if(this.editable){
18000                 this.tickableInputEl().focus();
18001             }
18002             
18003         }
18004         
18005         Roo.get(document).on('mousedown', this.collapseIf, this);
18006         Roo.get(document).on('mousewheel', this.collapseIf, this);
18007         if (!this.editable) {
18008             Roo.get(document).on('keydown', this.listKeyPress, this);
18009         }
18010         
18011         this.fireEvent('expand', this);
18012     },
18013
18014     // private
18015     // Implements the default empty TriggerField.onTriggerClick function
18016     onTriggerClick : function(e)
18017     {
18018         Roo.log('trigger click');
18019         
18020         if(this.disabled || !this.triggerList){
18021             return;
18022         }
18023         
18024         this.page = 0;
18025         this.loadNext = false;
18026         
18027         if(this.isExpanded()){
18028             this.collapse();
18029             if (!this.blockFocus) {
18030                 this.inputEl().focus();
18031             }
18032             
18033         }else {
18034             this.hasFocus = true;
18035             if(this.triggerAction == 'all') {
18036                 this.doQuery(this.allQuery, true);
18037             } else {
18038                 this.doQuery(this.getRawValue());
18039             }
18040             if (!this.blockFocus) {
18041                 this.inputEl().focus();
18042             }
18043         }
18044     },
18045     
18046     onTickableTriggerClick : function(e)
18047     {
18048         if(this.disabled){
18049             return;
18050         }
18051         
18052         this.page = 0;
18053         this.loadNext = false;
18054         this.hasFocus = true;
18055         
18056         if(this.triggerAction == 'all') {
18057             this.doQuery(this.allQuery, true);
18058         } else {
18059             this.doQuery(this.getRawValue());
18060         }
18061     },
18062     
18063     onSearchFieldClick : function(e)
18064     {
18065         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18066             this.onTickableFooterButtonClick(e, false, false);
18067             return;
18068         }
18069         
18070         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18071             return;
18072         }
18073         
18074         this.page = 0;
18075         this.loadNext = false;
18076         this.hasFocus = true;
18077         
18078         if(this.triggerAction == 'all') {
18079             this.doQuery(this.allQuery, true);
18080         } else {
18081             this.doQuery(this.getRawValue());
18082         }
18083     },
18084     
18085     listKeyPress : function(e)
18086     {
18087         //Roo.log('listkeypress');
18088         // scroll to first matching element based on key pres..
18089         if (e.isSpecialKey()) {
18090             return false;
18091         }
18092         var k = String.fromCharCode(e.getKey()).toUpperCase();
18093         //Roo.log(k);
18094         var match  = false;
18095         var csel = this.view.getSelectedNodes();
18096         var cselitem = false;
18097         if (csel.length) {
18098             var ix = this.view.indexOf(csel[0]);
18099             cselitem  = this.store.getAt(ix);
18100             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18101                 cselitem = false;
18102             }
18103             
18104         }
18105         
18106         this.store.each(function(v) { 
18107             if (cselitem) {
18108                 // start at existing selection.
18109                 if (cselitem.id == v.id) {
18110                     cselitem = false;
18111                 }
18112                 return true;
18113             }
18114                 
18115             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18116                 match = this.store.indexOf(v);
18117                 return false;
18118             }
18119             return true;
18120         }, this);
18121         
18122         if (match === false) {
18123             return true; // no more action?
18124         }
18125         // scroll to?
18126         this.view.select(match);
18127         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18128         sn.scrollIntoView(sn.dom.parentNode, false);
18129     },
18130     
18131     onViewScroll : function(e, t){
18132         
18133         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){
18134             return;
18135         }
18136         
18137         this.hasQuery = true;
18138         
18139         this.loading = this.list.select('.loading', true).first();
18140         
18141         if(this.loading === null){
18142             this.list.createChild({
18143                 tag: 'div',
18144                 cls: 'loading roo-select2-more-results roo-select2-active',
18145                 html: 'Loading more results...'
18146             });
18147             
18148             this.loading = this.list.select('.loading', true).first();
18149             
18150             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18151             
18152             this.loading.hide();
18153         }
18154         
18155         this.loading.show();
18156         
18157         var _combo = this;
18158         
18159         this.page++;
18160         this.loadNext = true;
18161         
18162         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18163         
18164         return;
18165     },
18166     
18167     addItem : function(o)
18168     {   
18169         var dv = ''; // display value
18170         
18171         if (this.displayField) {
18172             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18173         } else {
18174             // this is an error condition!!!
18175             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18176         }
18177         
18178         if(!dv.length){
18179             return;
18180         }
18181         
18182         var choice = this.choices.createChild({
18183             tag: 'li',
18184             cls: 'roo-select2-search-choice',
18185             cn: [
18186                 {
18187                     tag: 'div',
18188                     html: dv
18189                 },
18190                 {
18191                     tag: 'a',
18192                     href: '#',
18193                     cls: 'roo-select2-search-choice-close fa fa-times',
18194                     tabindex: '-1'
18195                 }
18196             ]
18197             
18198         }, this.searchField);
18199         
18200         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18201         
18202         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18203         
18204         this.item.push(o);
18205         
18206         this.lastData = o;
18207         
18208         this.syncValue();
18209         
18210         this.inputEl().dom.value = '';
18211         
18212         this.validate();
18213     },
18214     
18215     onRemoveItem : function(e, _self, o)
18216     {
18217         e.preventDefault();
18218         
18219         this.lastItem = Roo.apply([], this.item);
18220         
18221         var index = this.item.indexOf(o.data) * 1;
18222         
18223         if( index < 0){
18224             Roo.log('not this item?!');
18225             return;
18226         }
18227         
18228         this.item.splice(index, 1);
18229         o.item.remove();
18230         
18231         this.syncValue();
18232         
18233         this.fireEvent('remove', this, e);
18234         
18235         this.validate();
18236         
18237     },
18238     
18239     syncValue : function()
18240     {
18241         if(!this.item.length){
18242             this.clearValue();
18243             return;
18244         }
18245             
18246         var value = [];
18247         var _this = this;
18248         Roo.each(this.item, function(i){
18249             if(_this.valueField){
18250                 value.push(i[_this.valueField]);
18251                 return;
18252             }
18253
18254             value.push(i);
18255         });
18256
18257         this.value = value.join(',');
18258
18259         if(this.hiddenField){
18260             this.hiddenField.dom.value = this.value;
18261         }
18262         
18263         this.store.fireEvent("datachanged", this.store);
18264         
18265         this.validate();
18266     },
18267     
18268     clearItem : function()
18269     {
18270         if(!this.multiple){
18271             return;
18272         }
18273         
18274         this.item = [];
18275         
18276         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18277            c.remove();
18278         });
18279         
18280         this.syncValue();
18281         
18282         this.validate();
18283         
18284         if(this.tickable && !Roo.isTouch){
18285             this.view.refresh();
18286         }
18287     },
18288     
18289     inputEl: function ()
18290     {
18291         if(Roo.isIOS && this.useNativeIOS){
18292             return this.el.select('select.roo-ios-select', true).first();
18293         }
18294         
18295         if(Roo.isTouch && this.mobileTouchView){
18296             return this.el.select('input.form-control',true).first();
18297         }
18298         
18299         if(this.tickable){
18300             return this.searchField;
18301         }
18302         
18303         return this.el.select('input.form-control',true).first();
18304     },
18305     
18306     onTickableFooterButtonClick : function(e, btn, el)
18307     {
18308         e.preventDefault();
18309         
18310         this.lastItem = Roo.apply([], this.item);
18311         
18312         if(btn && btn.name == 'cancel'){
18313             this.tickItems = Roo.apply([], this.item);
18314             this.collapse();
18315             return;
18316         }
18317         
18318         this.clearItem();
18319         
18320         var _this = this;
18321         
18322         Roo.each(this.tickItems, function(o){
18323             _this.addItem(o);
18324         });
18325         
18326         this.collapse();
18327         
18328     },
18329     
18330     validate : function()
18331     {
18332         if(this.getVisibilityEl().hasClass('hidden')){
18333             return true;
18334         }
18335         
18336         var v = this.getRawValue();
18337         
18338         if(this.multiple){
18339             v = this.getValue();
18340         }
18341         
18342         if(this.disabled || this.allowBlank || v.length){
18343             this.markValid();
18344             return true;
18345         }
18346         
18347         this.markInvalid();
18348         return false;
18349     },
18350     
18351     tickableInputEl : function()
18352     {
18353         if(!this.tickable || !this.editable){
18354             return this.inputEl();
18355         }
18356         
18357         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18358     },
18359     
18360     
18361     getAutoCreateTouchView : function()
18362     {
18363         var id = Roo.id();
18364         
18365         var cfg = {
18366             cls: 'form-group' //input-group
18367         };
18368         
18369         var input =  {
18370             tag: 'input',
18371             id : id,
18372             type : this.inputType,
18373             cls : 'form-control x-combo-noedit',
18374             autocomplete: 'new-password',
18375             placeholder : this.placeholder || '',
18376             readonly : true
18377         };
18378         
18379         if (this.name) {
18380             input.name = this.name;
18381         }
18382         
18383         if (this.size) {
18384             input.cls += ' input-' + this.size;
18385         }
18386         
18387         if (this.disabled) {
18388             input.disabled = true;
18389         }
18390         
18391         var inputblock = {
18392             cls : 'roo-combobox-wrap',
18393             cn : [
18394                 input
18395             ]
18396         };
18397         
18398         if(this.before){
18399             inputblock.cls += ' input-group';
18400             
18401             inputblock.cn.unshift({
18402                 tag :'span',
18403                 cls : 'input-group-addon input-group-prepend input-group-text',
18404                 html : this.before
18405             });
18406         }
18407         
18408         if(this.removable && !this.multiple){
18409             inputblock.cls += ' roo-removable';
18410             
18411             inputblock.cn.push({
18412                 tag: 'button',
18413                 html : 'x',
18414                 cls : 'roo-combo-removable-btn close'
18415             });
18416         }
18417
18418         if(this.hasFeedback && !this.allowBlank){
18419             
18420             inputblock.cls += ' has-feedback';
18421             
18422             inputblock.cn.push({
18423                 tag: 'span',
18424                 cls: 'glyphicon form-control-feedback'
18425             });
18426             
18427         }
18428         
18429         if (this.after) {
18430             
18431             inputblock.cls += (this.before) ? '' : ' input-group';
18432             
18433             inputblock.cn.push({
18434                 tag :'span',
18435                 cls : 'input-group-addon input-group-append input-group-text',
18436                 html : this.after
18437             });
18438         }
18439
18440         
18441         var ibwrap = inputblock;
18442         
18443         if(this.multiple){
18444             ibwrap = {
18445                 tag: 'ul',
18446                 cls: 'roo-select2-choices',
18447                 cn:[
18448                     {
18449                         tag: 'li',
18450                         cls: 'roo-select2-search-field',
18451                         cn: [
18452
18453                             inputblock
18454                         ]
18455                     }
18456                 ]
18457             };
18458         
18459             
18460         }
18461         
18462         var combobox = {
18463             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18464             cn: [
18465                 {
18466                     tag: 'input',
18467                     type : 'hidden',
18468                     cls: 'form-hidden-field'
18469                 },
18470                 ibwrap
18471             ]
18472         };
18473         
18474         if(!this.multiple && this.showToggleBtn){
18475             
18476             var caret = {
18477                 cls: 'caret'
18478             };
18479             
18480             if (this.caret != false) {
18481                 caret = {
18482                      tag: 'i',
18483                      cls: 'fa fa-' + this.caret
18484                 };
18485                 
18486             }
18487             
18488             combobox.cn.push({
18489                 tag :'span',
18490                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18491                 cn : [
18492                     Roo.bootstrap.version == 3 ? caret : '',
18493                     {
18494                         tag: 'span',
18495                         cls: 'combobox-clear',
18496                         cn  : [
18497                             {
18498                                 tag : 'i',
18499                                 cls: 'icon-remove'
18500                             }
18501                         ]
18502                     }
18503                 ]
18504
18505             })
18506         }
18507         
18508         if(this.multiple){
18509             combobox.cls += ' roo-select2-container-multi';
18510         }
18511         
18512         var required =  this.allowBlank ?  {
18513                     tag : 'i',
18514                     style: 'display: none'
18515                 } : {
18516                    tag : 'i',
18517                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18518                    tooltip : 'This field is required'
18519                 };
18520         
18521         var align = this.labelAlign || this.parentLabelAlign();
18522         
18523         if (align ==='left' && this.fieldLabel.length) {
18524
18525             cfg.cn = [
18526                 required,
18527                 {
18528                     tag: 'label',
18529                     cls : 'control-label col-form-label',
18530                     html : this.fieldLabel
18531
18532                 },
18533                 {
18534                     cls : 'roo-combobox-wrap ', 
18535                     cn: [
18536                         combobox
18537                     ]
18538                 }
18539             ];
18540             
18541             var labelCfg = cfg.cn[1];
18542             var contentCfg = cfg.cn[2];
18543             
18544
18545             if(this.indicatorpos == 'right'){
18546                 cfg.cn = [
18547                     {
18548                         tag: 'label',
18549                         'for' :  id,
18550                         cls : 'control-label col-form-label',
18551                         cn : [
18552                             {
18553                                 tag : 'span',
18554                                 html : this.fieldLabel
18555                             },
18556                             required
18557                         ]
18558                     },
18559                     {
18560                         cls : "roo-combobox-wrap ",
18561                         cn: [
18562                             combobox
18563                         ]
18564                     }
18565
18566                 ];
18567                 
18568                 labelCfg = cfg.cn[0];
18569                 contentCfg = cfg.cn[1];
18570             }
18571             
18572            
18573             
18574             if(this.labelWidth > 12){
18575                 labelCfg.style = "width: " + this.labelWidth + 'px';
18576             }
18577            
18578             if(this.labelWidth < 13 && this.labelmd == 0){
18579                 this.labelmd = this.labelWidth;
18580             }
18581             
18582             if(this.labellg > 0){
18583                 labelCfg.cls += ' col-lg-' + this.labellg;
18584                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18585             }
18586             
18587             if(this.labelmd > 0){
18588                 labelCfg.cls += ' col-md-' + this.labelmd;
18589                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18590             }
18591             
18592             if(this.labelsm > 0){
18593                 labelCfg.cls += ' col-sm-' + this.labelsm;
18594                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18595             }
18596             
18597             if(this.labelxs > 0){
18598                 labelCfg.cls += ' col-xs-' + this.labelxs;
18599                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18600             }
18601                 
18602                 
18603         } else if ( this.fieldLabel.length) {
18604             cfg.cn = [
18605                required,
18606                 {
18607                     tag: 'label',
18608                     cls : 'control-label',
18609                     html : this.fieldLabel
18610
18611                 },
18612                 {
18613                     cls : '', 
18614                     cn: [
18615                         combobox
18616                     ]
18617                 }
18618             ];
18619             
18620             if(this.indicatorpos == 'right'){
18621                 cfg.cn = [
18622                     {
18623                         tag: 'label',
18624                         cls : 'control-label',
18625                         html : this.fieldLabel,
18626                         cn : [
18627                             required
18628                         ]
18629                     },
18630                     {
18631                         cls : '', 
18632                         cn: [
18633                             combobox
18634                         ]
18635                     }
18636                 ];
18637             }
18638         } else {
18639             cfg.cn = combobox;    
18640         }
18641         
18642         
18643         var settings = this;
18644         
18645         ['xs','sm','md','lg'].map(function(size){
18646             if (settings[size]) {
18647                 cfg.cls += ' col-' + size + '-' + settings[size];
18648             }
18649         });
18650         
18651         return cfg;
18652     },
18653     
18654     initTouchView : function()
18655     {
18656         this.renderTouchView();
18657         
18658         this.touchViewEl.on('scroll', function(){
18659             this.el.dom.scrollTop = 0;
18660         }, this);
18661         
18662         this.originalValue = this.getValue();
18663         
18664         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18665         
18666         this.inputEl().on("click", this.showTouchView, this);
18667         if (this.triggerEl) {
18668             this.triggerEl.on("click", this.showTouchView, this);
18669         }
18670         
18671         
18672         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18673         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18674         
18675         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18676         
18677         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18678         this.store.on('load', this.onTouchViewLoad, this);
18679         this.store.on('loadexception', this.onTouchViewLoadException, this);
18680         
18681         if(this.hiddenName){
18682             
18683             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18684             
18685             this.hiddenField.dom.value =
18686                 this.hiddenValue !== undefined ? this.hiddenValue :
18687                 this.value !== undefined ? this.value : '';
18688         
18689             this.el.dom.removeAttribute('name');
18690             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18691         }
18692         
18693         if(this.multiple){
18694             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18695             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18696         }
18697         
18698         if(this.removable && !this.multiple){
18699             var close = this.closeTriggerEl();
18700             if(close){
18701                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18702                 close.on('click', this.removeBtnClick, this, close);
18703             }
18704         }
18705         /*
18706          * fix the bug in Safari iOS8
18707          */
18708         this.inputEl().on("focus", function(e){
18709             document.activeElement.blur();
18710         }, this);
18711         
18712         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18713         
18714         return;
18715         
18716         
18717     },
18718     
18719     renderTouchView : function()
18720     {
18721         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18722         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18723         
18724         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18725         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18726         
18727         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18728         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18729         this.touchViewBodyEl.setStyle('overflow', 'auto');
18730         
18731         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18732         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18733         
18734         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18735         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18736         
18737     },
18738     
18739     showTouchView : function()
18740     {
18741         if(this.disabled){
18742             return;
18743         }
18744         
18745         this.touchViewHeaderEl.hide();
18746
18747         if(this.modalTitle.length){
18748             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18749             this.touchViewHeaderEl.show();
18750         }
18751
18752         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18753         this.touchViewEl.show();
18754
18755         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18756         
18757         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18758         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18759
18760         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18761
18762         if(this.modalTitle.length){
18763             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18764         }
18765         
18766         this.touchViewBodyEl.setHeight(bodyHeight);
18767
18768         if(this.animate){
18769             var _this = this;
18770             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18771         }else{
18772             this.touchViewEl.addClass(['in','show']);
18773         }
18774         
18775         if(this._touchViewMask){
18776             Roo.get(document.body).addClass("x-body-masked");
18777             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18778             this._touchViewMask.setStyle('z-index', 10000);
18779             this._touchViewMask.addClass('show');
18780         }
18781         
18782         this.doTouchViewQuery();
18783         
18784     },
18785     
18786     hideTouchView : function()
18787     {
18788         this.touchViewEl.removeClass(['in','show']);
18789
18790         if(this.animate){
18791             var _this = this;
18792             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18793         }else{
18794             this.touchViewEl.setStyle('display', 'none');
18795         }
18796         
18797         if(this._touchViewMask){
18798             this._touchViewMask.removeClass('show');
18799             Roo.get(document.body).removeClass("x-body-masked");
18800         }
18801     },
18802     
18803     setTouchViewValue : function()
18804     {
18805         if(this.multiple){
18806             this.clearItem();
18807         
18808             var _this = this;
18809
18810             Roo.each(this.tickItems, function(o){
18811                 this.addItem(o);
18812             }, this);
18813         }
18814         
18815         this.hideTouchView();
18816     },
18817     
18818     doTouchViewQuery : function()
18819     {
18820         var qe = {
18821             query: '',
18822             forceAll: true,
18823             combo: this,
18824             cancel:false
18825         };
18826         
18827         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18828             return false;
18829         }
18830         
18831         if(!this.alwaysQuery || this.mode == 'local'){
18832             this.onTouchViewLoad();
18833             return;
18834         }
18835         
18836         this.store.load();
18837     },
18838     
18839     onTouchViewBeforeLoad : function(combo,opts)
18840     {
18841         return;
18842     },
18843
18844     // private
18845     onTouchViewLoad : function()
18846     {
18847         if(this.store.getCount() < 1){
18848             this.onTouchViewEmptyResults();
18849             return;
18850         }
18851         
18852         this.clearTouchView();
18853         
18854         var rawValue = this.getRawValue();
18855         
18856         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18857         
18858         this.tickItems = [];
18859         
18860         this.store.data.each(function(d, rowIndex){
18861             var row = this.touchViewListGroup.createChild(template);
18862             
18863             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18864                 row.addClass(d.data.cls);
18865             }
18866             
18867             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18868                 var cfg = {
18869                     data : d.data,
18870                     html : d.data[this.displayField]
18871                 };
18872                 
18873                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18874                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18875                 }
18876             }
18877             row.removeClass('selected');
18878             if(!this.multiple && this.valueField &&
18879                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18880             {
18881                 // radio buttons..
18882                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18883                 row.addClass('selected');
18884             }
18885             
18886             if(this.multiple && this.valueField &&
18887                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18888             {
18889                 
18890                 // checkboxes...
18891                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18892                 this.tickItems.push(d.data);
18893             }
18894             
18895             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18896             
18897         }, this);
18898         
18899         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18900         
18901         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18902
18903         if(this.modalTitle.length){
18904             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18905         }
18906
18907         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18908         
18909         if(this.mobile_restrict_height && listHeight < bodyHeight){
18910             this.touchViewBodyEl.setHeight(listHeight);
18911         }
18912         
18913         var _this = this;
18914         
18915         if(firstChecked && listHeight > bodyHeight){
18916             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18917         }
18918         
18919     },
18920     
18921     onTouchViewLoadException : function()
18922     {
18923         this.hideTouchView();
18924     },
18925     
18926     onTouchViewEmptyResults : function()
18927     {
18928         this.clearTouchView();
18929         
18930         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18931         
18932         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18933         
18934     },
18935     
18936     clearTouchView : function()
18937     {
18938         this.touchViewListGroup.dom.innerHTML = '';
18939     },
18940     
18941     onTouchViewClick : function(e, el, o)
18942     {
18943         e.preventDefault();
18944         
18945         var row = o.row;
18946         var rowIndex = o.rowIndex;
18947         
18948         var r = this.store.getAt(rowIndex);
18949         
18950         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18951             
18952             if(!this.multiple){
18953                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18954                     c.dom.removeAttribute('checked');
18955                 }, this);
18956
18957                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18958
18959                 this.setFromData(r.data);
18960
18961                 var close = this.closeTriggerEl();
18962
18963                 if(close){
18964                     close.show();
18965                 }
18966
18967                 this.hideTouchView();
18968
18969                 this.fireEvent('select', this, r, rowIndex);
18970
18971                 return;
18972             }
18973
18974             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18975                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18976                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18977                 return;
18978             }
18979
18980             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18981             this.addItem(r.data);
18982             this.tickItems.push(r.data);
18983         }
18984     },
18985     
18986     getAutoCreateNativeIOS : function()
18987     {
18988         var cfg = {
18989             cls: 'form-group' //input-group,
18990         };
18991         
18992         var combobox =  {
18993             tag: 'select',
18994             cls : 'roo-ios-select'
18995         };
18996         
18997         if (this.name) {
18998             combobox.name = this.name;
18999         }
19000         
19001         if (this.disabled) {
19002             combobox.disabled = true;
19003         }
19004         
19005         var settings = this;
19006         
19007         ['xs','sm','md','lg'].map(function(size){
19008             if (settings[size]) {
19009                 cfg.cls += ' col-' + size + '-' + settings[size];
19010             }
19011         });
19012         
19013         cfg.cn = combobox;
19014         
19015         return cfg;
19016         
19017     },
19018     
19019     initIOSView : function()
19020     {
19021         this.store.on('load', this.onIOSViewLoad, this);
19022         
19023         return;
19024     },
19025     
19026     onIOSViewLoad : function()
19027     {
19028         if(this.store.getCount() < 1){
19029             return;
19030         }
19031         
19032         this.clearIOSView();
19033         
19034         if(this.allowBlank) {
19035             
19036             var default_text = '-- SELECT --';
19037             
19038             if(this.placeholder.length){
19039                 default_text = this.placeholder;
19040             }
19041             
19042             if(this.emptyTitle.length){
19043                 default_text += ' - ' + this.emptyTitle + ' -';
19044             }
19045             
19046             var opt = this.inputEl().createChild({
19047                 tag: 'option',
19048                 value : 0,
19049                 html : default_text
19050             });
19051             
19052             var o = {};
19053             o[this.valueField] = 0;
19054             o[this.displayField] = default_text;
19055             
19056             this.ios_options.push({
19057                 data : o,
19058                 el : opt
19059             });
19060             
19061         }
19062         
19063         this.store.data.each(function(d, rowIndex){
19064             
19065             var html = '';
19066             
19067             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19068                 html = d.data[this.displayField];
19069             }
19070             
19071             var value = '';
19072             
19073             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19074                 value = d.data[this.valueField];
19075             }
19076             
19077             var option = {
19078                 tag: 'option',
19079                 value : value,
19080                 html : html
19081             };
19082             
19083             if(this.value == d.data[this.valueField]){
19084                 option['selected'] = true;
19085             }
19086             
19087             var opt = this.inputEl().createChild(option);
19088             
19089             this.ios_options.push({
19090                 data : d.data,
19091                 el : opt
19092             });
19093             
19094         }, this);
19095         
19096         this.inputEl().on('change', function(){
19097            this.fireEvent('select', this);
19098         }, this);
19099         
19100     },
19101     
19102     clearIOSView: function()
19103     {
19104         this.inputEl().dom.innerHTML = '';
19105         
19106         this.ios_options = [];
19107     },
19108     
19109     setIOSValue: function(v)
19110     {
19111         this.value = v;
19112         
19113         if(!this.ios_options){
19114             return;
19115         }
19116         
19117         Roo.each(this.ios_options, function(opts){
19118            
19119            opts.el.dom.removeAttribute('selected');
19120            
19121            if(opts.data[this.valueField] != v){
19122                return;
19123            }
19124            
19125            opts.el.dom.setAttribute('selected', true);
19126            
19127         }, this);
19128     }
19129
19130     /** 
19131     * @cfg {Boolean} grow 
19132     * @hide 
19133     */
19134     /** 
19135     * @cfg {Number} growMin 
19136     * @hide 
19137     */
19138     /** 
19139     * @cfg {Number} growMax 
19140     * @hide 
19141     */
19142     /**
19143      * @hide
19144      * @method autoSize
19145      */
19146 });
19147
19148 Roo.apply(Roo.bootstrap.ComboBox,  {
19149     
19150     header : {
19151         tag: 'div',
19152         cls: 'modal-header',
19153         cn: [
19154             {
19155                 tag: 'h4',
19156                 cls: 'modal-title'
19157             }
19158         ]
19159     },
19160     
19161     body : {
19162         tag: 'div',
19163         cls: 'modal-body',
19164         cn: [
19165             {
19166                 tag: 'ul',
19167                 cls: 'list-group'
19168             }
19169         ]
19170     },
19171     
19172     listItemRadio : {
19173         tag: 'li',
19174         cls: 'list-group-item',
19175         cn: [
19176             {
19177                 tag: 'span',
19178                 cls: 'roo-combobox-list-group-item-value'
19179             },
19180             {
19181                 tag: 'div',
19182                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19183                 cn: [
19184                     {
19185                         tag: 'input',
19186                         type: 'radio'
19187                     },
19188                     {
19189                         tag: 'label'
19190                     }
19191                 ]
19192             }
19193         ]
19194     },
19195     
19196     listItemCheckbox : {
19197         tag: 'li',
19198         cls: 'list-group-item',
19199         cn: [
19200             {
19201                 tag: 'span',
19202                 cls: 'roo-combobox-list-group-item-value'
19203             },
19204             {
19205                 tag: 'div',
19206                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19207                 cn: [
19208                     {
19209                         tag: 'input',
19210                         type: 'checkbox'
19211                     },
19212                     {
19213                         tag: 'label'
19214                     }
19215                 ]
19216             }
19217         ]
19218     },
19219     
19220     emptyResult : {
19221         tag: 'div',
19222         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19223     },
19224     
19225     footer : {
19226         tag: 'div',
19227         cls: 'modal-footer',
19228         cn: [
19229             {
19230                 tag: 'div',
19231                 cls: 'row',
19232                 cn: [
19233                     {
19234                         tag: 'div',
19235                         cls: 'col-xs-6 text-left',
19236                         cn: {
19237                             tag: 'button',
19238                             cls: 'btn btn-danger roo-touch-view-cancel',
19239                             html: 'Cancel'
19240                         }
19241                     },
19242                     {
19243                         tag: 'div',
19244                         cls: 'col-xs-6 text-right',
19245                         cn: {
19246                             tag: 'button',
19247                             cls: 'btn btn-success roo-touch-view-ok',
19248                             html: 'OK'
19249                         }
19250                     }
19251                 ]
19252             }
19253         ]
19254         
19255     }
19256 });
19257
19258 Roo.apply(Roo.bootstrap.ComboBox,  {
19259     
19260     touchViewTemplate : {
19261         tag: 'div',
19262         cls: 'modal fade roo-combobox-touch-view',
19263         cn: [
19264             {
19265                 tag: 'div',
19266                 cls: 'modal-dialog',
19267                 style : 'position:fixed', // we have to fix position....
19268                 cn: [
19269                     {
19270                         tag: 'div',
19271                         cls: 'modal-content',
19272                         cn: [
19273                             Roo.bootstrap.ComboBox.header,
19274                             Roo.bootstrap.ComboBox.body,
19275                             Roo.bootstrap.ComboBox.footer
19276                         ]
19277                     }
19278                 ]
19279             }
19280         ]
19281     }
19282 });/*
19283  * Based on:
19284  * Ext JS Library 1.1.1
19285  * Copyright(c) 2006-2007, Ext JS, LLC.
19286  *
19287  * Originally Released Under LGPL - original licence link has changed is not relivant.
19288  *
19289  * Fork - LGPL
19290  * <script type="text/javascript">
19291  */
19292
19293 /**
19294  * @class Roo.View
19295  * @extends Roo.util.Observable
19296  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19297  * This class also supports single and multi selection modes. <br>
19298  * Create a data model bound view:
19299  <pre><code>
19300  var store = new Roo.data.Store(...);
19301
19302  var view = new Roo.View({
19303     el : "my-element",
19304     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19305  
19306     singleSelect: true,
19307     selectedClass: "ydataview-selected",
19308     store: store
19309  });
19310
19311  // listen for node click?
19312  view.on("click", function(vw, index, node, e){
19313  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19314  });
19315
19316  // load XML data
19317  dataModel.load("foobar.xml");
19318  </code></pre>
19319  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19320  * <br><br>
19321  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19322  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19323  * 
19324  * Note: old style constructor is still suported (container, template, config)
19325  * 
19326  * @constructor
19327  * Create a new View
19328  * @param {Object} config The config object
19329  * 
19330  */
19331 Roo.View = function(config, depreciated_tpl, depreciated_config){
19332     
19333     this.parent = false;
19334     
19335     if (typeof(depreciated_tpl) == 'undefined') {
19336         // new way.. - universal constructor.
19337         Roo.apply(this, config);
19338         this.el  = Roo.get(this.el);
19339     } else {
19340         // old format..
19341         this.el  = Roo.get(config);
19342         this.tpl = depreciated_tpl;
19343         Roo.apply(this, depreciated_config);
19344     }
19345     this.wrapEl  = this.el.wrap().wrap();
19346     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19347     
19348     
19349     if(typeof(this.tpl) == "string"){
19350         this.tpl = new Roo.Template(this.tpl);
19351     } else {
19352         // support xtype ctors..
19353         this.tpl = new Roo.factory(this.tpl, Roo);
19354     }
19355     
19356     
19357     this.tpl.compile();
19358     
19359     /** @private */
19360     this.addEvents({
19361         /**
19362          * @event beforeclick
19363          * Fires before a click is processed. Returns false to cancel the default action.
19364          * @param {Roo.View} this
19365          * @param {Number} index The index of the target node
19366          * @param {HTMLElement} node The target node
19367          * @param {Roo.EventObject} e The raw event object
19368          */
19369             "beforeclick" : true,
19370         /**
19371          * @event click
19372          * Fires when a template node is clicked.
19373          * @param {Roo.View} this
19374          * @param {Number} index The index of the target node
19375          * @param {HTMLElement} node The target node
19376          * @param {Roo.EventObject} e The raw event object
19377          */
19378             "click" : true,
19379         /**
19380          * @event dblclick
19381          * Fires when a template node is double clicked.
19382          * @param {Roo.View} this
19383          * @param {Number} index The index of the target node
19384          * @param {HTMLElement} node The target node
19385          * @param {Roo.EventObject} e The raw event object
19386          */
19387             "dblclick" : true,
19388         /**
19389          * @event contextmenu
19390          * Fires when a template node is right clicked.
19391          * @param {Roo.View} this
19392          * @param {Number} index The index of the target node
19393          * @param {HTMLElement} node The target node
19394          * @param {Roo.EventObject} e The raw event object
19395          */
19396             "contextmenu" : true,
19397         /**
19398          * @event selectionchange
19399          * Fires when the selected nodes change.
19400          * @param {Roo.View} this
19401          * @param {Array} selections Array of the selected nodes
19402          */
19403             "selectionchange" : true,
19404     
19405         /**
19406          * @event beforeselect
19407          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19408          * @param {Roo.View} this
19409          * @param {HTMLElement} node The node to be selected
19410          * @param {Array} selections Array of currently selected nodes
19411          */
19412             "beforeselect" : true,
19413         /**
19414          * @event preparedata
19415          * Fires on every row to render, to allow you to change the data.
19416          * @param {Roo.View} this
19417          * @param {Object} data to be rendered (change this)
19418          */
19419           "preparedata" : true
19420           
19421           
19422         });
19423
19424
19425
19426     this.el.on({
19427         "click": this.onClick,
19428         "dblclick": this.onDblClick,
19429         "contextmenu": this.onContextMenu,
19430         scope:this
19431     });
19432
19433     this.selections = [];
19434     this.nodes = [];
19435     this.cmp = new Roo.CompositeElementLite([]);
19436     if(this.store){
19437         this.store = Roo.factory(this.store, Roo.data);
19438         this.setStore(this.store, true);
19439     }
19440     
19441     if ( this.footer && this.footer.xtype) {
19442            
19443          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19444         
19445         this.footer.dataSource = this.store;
19446         this.footer.container = fctr;
19447         this.footer = Roo.factory(this.footer, Roo);
19448         fctr.insertFirst(this.el);
19449         
19450         // this is a bit insane - as the paging toolbar seems to detach the el..
19451 //        dom.parentNode.parentNode.parentNode
19452          // they get detached?
19453     }
19454     
19455     
19456     Roo.View.superclass.constructor.call(this);
19457     
19458     
19459 };
19460
19461 Roo.extend(Roo.View, Roo.util.Observable, {
19462     
19463      /**
19464      * @cfg {Roo.data.Store} store Data store to load data from.
19465      */
19466     store : false,
19467     
19468     /**
19469      * @cfg {String|Roo.Element} el The container element.
19470      */
19471     el : '',
19472     
19473     /**
19474      * @cfg {String|Roo.Template} tpl The template used by this View 
19475      */
19476     tpl : false,
19477     /**
19478      * @cfg {String} dataName the named area of the template to use as the data area
19479      *                          Works with domtemplates roo-name="name"
19480      */
19481     dataName: false,
19482     /**
19483      * @cfg {String} selectedClass The css class to add to selected nodes
19484      */
19485     selectedClass : "x-view-selected",
19486      /**
19487      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19488      */
19489     emptyText : "",
19490     
19491     /**
19492      * @cfg {String} text to display on mask (default Loading)
19493      */
19494     mask : false,
19495     /**
19496      * @cfg {Boolean} multiSelect Allow multiple selection
19497      */
19498     multiSelect : false,
19499     /**
19500      * @cfg {Boolean} singleSelect Allow single selection
19501      */
19502     singleSelect:  false,
19503     
19504     /**
19505      * @cfg {Boolean} toggleSelect - selecting 
19506      */
19507     toggleSelect : false,
19508     
19509     /**
19510      * @cfg {Boolean} tickable - selecting 
19511      */
19512     tickable : false,
19513     
19514     /**
19515      * Returns the element this view is bound to.
19516      * @return {Roo.Element}
19517      */
19518     getEl : function(){
19519         return this.wrapEl;
19520     },
19521     
19522     
19523
19524     /**
19525      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19526      */
19527     refresh : function(){
19528         //Roo.log('refresh');
19529         var t = this.tpl;
19530         
19531         // if we are using something like 'domtemplate', then
19532         // the what gets used is:
19533         // t.applySubtemplate(NAME, data, wrapping data..)
19534         // the outer template then get' applied with
19535         //     the store 'extra data'
19536         // and the body get's added to the
19537         //      roo-name="data" node?
19538         //      <span class='roo-tpl-{name}'></span> ?????
19539         
19540         
19541         
19542         this.clearSelections();
19543         this.el.update("");
19544         var html = [];
19545         var records = this.store.getRange();
19546         if(records.length < 1) {
19547             
19548             // is this valid??  = should it render a template??
19549             
19550             this.el.update(this.emptyText);
19551             return;
19552         }
19553         var el = this.el;
19554         if (this.dataName) {
19555             this.el.update(t.apply(this.store.meta)); //????
19556             el = this.el.child('.roo-tpl-' + this.dataName);
19557         }
19558         
19559         for(var i = 0, len = records.length; i < len; i++){
19560             var data = this.prepareData(records[i].data, i, records[i]);
19561             this.fireEvent("preparedata", this, data, i, records[i]);
19562             
19563             var d = Roo.apply({}, data);
19564             
19565             if(this.tickable){
19566                 Roo.apply(d, {'roo-id' : Roo.id()});
19567                 
19568                 var _this = this;
19569             
19570                 Roo.each(this.parent.item, function(item){
19571                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19572                         return;
19573                     }
19574                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19575                 });
19576             }
19577             
19578             html[html.length] = Roo.util.Format.trim(
19579                 this.dataName ?
19580                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19581                     t.apply(d)
19582             );
19583         }
19584         
19585         
19586         
19587         el.update(html.join(""));
19588         this.nodes = el.dom.childNodes;
19589         this.updateIndexes(0);
19590     },
19591     
19592
19593     /**
19594      * Function to override to reformat the data that is sent to
19595      * the template for each node.
19596      * DEPRICATED - use the preparedata event handler.
19597      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19598      * a JSON object for an UpdateManager bound view).
19599      */
19600     prepareData : function(data, index, record)
19601     {
19602         this.fireEvent("preparedata", this, data, index, record);
19603         return data;
19604     },
19605
19606     onUpdate : function(ds, record){
19607         // Roo.log('on update');   
19608         this.clearSelections();
19609         var index = this.store.indexOf(record);
19610         var n = this.nodes[index];
19611         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19612         n.parentNode.removeChild(n);
19613         this.updateIndexes(index, index);
19614     },
19615
19616     
19617     
19618 // --------- FIXME     
19619     onAdd : function(ds, records, index)
19620     {
19621         //Roo.log(['on Add', ds, records, index] );        
19622         this.clearSelections();
19623         if(this.nodes.length == 0){
19624             this.refresh();
19625             return;
19626         }
19627         var n = this.nodes[index];
19628         for(var i = 0, len = records.length; i < len; i++){
19629             var d = this.prepareData(records[i].data, i, records[i]);
19630             if(n){
19631                 this.tpl.insertBefore(n, d);
19632             }else{
19633                 
19634                 this.tpl.append(this.el, d);
19635             }
19636         }
19637         this.updateIndexes(index);
19638     },
19639
19640     onRemove : function(ds, record, index){
19641        // Roo.log('onRemove');
19642         this.clearSelections();
19643         var el = this.dataName  ?
19644             this.el.child('.roo-tpl-' + this.dataName) :
19645             this.el; 
19646         
19647         el.dom.removeChild(this.nodes[index]);
19648         this.updateIndexes(index);
19649     },
19650
19651     /**
19652      * Refresh an individual node.
19653      * @param {Number} index
19654      */
19655     refreshNode : function(index){
19656         this.onUpdate(this.store, this.store.getAt(index));
19657     },
19658
19659     updateIndexes : function(startIndex, endIndex){
19660         var ns = this.nodes;
19661         startIndex = startIndex || 0;
19662         endIndex = endIndex || ns.length - 1;
19663         for(var i = startIndex; i <= endIndex; i++){
19664             ns[i].nodeIndex = i;
19665         }
19666     },
19667
19668     /**
19669      * Changes the data store this view uses and refresh the view.
19670      * @param {Store} store
19671      */
19672     setStore : function(store, initial){
19673         if(!initial && this.store){
19674             this.store.un("datachanged", this.refresh);
19675             this.store.un("add", this.onAdd);
19676             this.store.un("remove", this.onRemove);
19677             this.store.un("update", this.onUpdate);
19678             this.store.un("clear", this.refresh);
19679             this.store.un("beforeload", this.onBeforeLoad);
19680             this.store.un("load", this.onLoad);
19681             this.store.un("loadexception", this.onLoad);
19682         }
19683         if(store){
19684           
19685             store.on("datachanged", this.refresh, this);
19686             store.on("add", this.onAdd, this);
19687             store.on("remove", this.onRemove, this);
19688             store.on("update", this.onUpdate, this);
19689             store.on("clear", this.refresh, this);
19690             store.on("beforeload", this.onBeforeLoad, this);
19691             store.on("load", this.onLoad, this);
19692             store.on("loadexception", this.onLoad, this);
19693         }
19694         
19695         if(store){
19696             this.refresh();
19697         }
19698     },
19699     /**
19700      * onbeforeLoad - masks the loading area.
19701      *
19702      */
19703     onBeforeLoad : function(store,opts)
19704     {
19705          //Roo.log('onBeforeLoad');   
19706         if (!opts.add) {
19707             this.el.update("");
19708         }
19709         this.el.mask(this.mask ? this.mask : "Loading" ); 
19710     },
19711     onLoad : function ()
19712     {
19713         this.el.unmask();
19714     },
19715     
19716
19717     /**
19718      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19719      * @param {HTMLElement} node
19720      * @return {HTMLElement} The template node
19721      */
19722     findItemFromChild : function(node){
19723         var el = this.dataName  ?
19724             this.el.child('.roo-tpl-' + this.dataName,true) :
19725             this.el.dom; 
19726         
19727         if(!node || node.parentNode == el){
19728                     return node;
19729             }
19730             var p = node.parentNode;
19731             while(p && p != el){
19732             if(p.parentNode == el){
19733                 return p;
19734             }
19735             p = p.parentNode;
19736         }
19737             return null;
19738     },
19739
19740     /** @ignore */
19741     onClick : function(e){
19742         var item = this.findItemFromChild(e.getTarget());
19743         if(item){
19744             var index = this.indexOf(item);
19745             if(this.onItemClick(item, index, e) !== false){
19746                 this.fireEvent("click", this, index, item, e);
19747             }
19748         }else{
19749             this.clearSelections();
19750         }
19751     },
19752
19753     /** @ignore */
19754     onContextMenu : function(e){
19755         var item = this.findItemFromChild(e.getTarget());
19756         if(item){
19757             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19758         }
19759     },
19760
19761     /** @ignore */
19762     onDblClick : function(e){
19763         var item = this.findItemFromChild(e.getTarget());
19764         if(item){
19765             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19766         }
19767     },
19768
19769     onItemClick : function(item, index, e)
19770     {
19771         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19772             return false;
19773         }
19774         if (this.toggleSelect) {
19775             var m = this.isSelected(item) ? 'unselect' : 'select';
19776             //Roo.log(m);
19777             var _t = this;
19778             _t[m](item, true, false);
19779             return true;
19780         }
19781         if(this.multiSelect || this.singleSelect){
19782             if(this.multiSelect && e.shiftKey && this.lastSelection){
19783                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19784             }else{
19785                 this.select(item, this.multiSelect && e.ctrlKey);
19786                 this.lastSelection = item;
19787             }
19788             
19789             if(!this.tickable){
19790                 e.preventDefault();
19791             }
19792             
19793         }
19794         return true;
19795     },
19796
19797     /**
19798      * Get the number of selected nodes.
19799      * @return {Number}
19800      */
19801     getSelectionCount : function(){
19802         return this.selections.length;
19803     },
19804
19805     /**
19806      * Get the currently selected nodes.
19807      * @return {Array} An array of HTMLElements
19808      */
19809     getSelectedNodes : function(){
19810         return this.selections;
19811     },
19812
19813     /**
19814      * Get the indexes of the selected nodes.
19815      * @return {Array}
19816      */
19817     getSelectedIndexes : function(){
19818         var indexes = [], s = this.selections;
19819         for(var i = 0, len = s.length; i < len; i++){
19820             indexes.push(s[i].nodeIndex);
19821         }
19822         return indexes;
19823     },
19824
19825     /**
19826      * Clear all selections
19827      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19828      */
19829     clearSelections : function(suppressEvent){
19830         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19831             this.cmp.elements = this.selections;
19832             this.cmp.removeClass(this.selectedClass);
19833             this.selections = [];
19834             if(!suppressEvent){
19835                 this.fireEvent("selectionchange", this, this.selections);
19836             }
19837         }
19838     },
19839
19840     /**
19841      * Returns true if the passed node is selected
19842      * @param {HTMLElement/Number} node The node or node index
19843      * @return {Boolean}
19844      */
19845     isSelected : function(node){
19846         var s = this.selections;
19847         if(s.length < 1){
19848             return false;
19849         }
19850         node = this.getNode(node);
19851         return s.indexOf(node) !== -1;
19852     },
19853
19854     /**
19855      * Selects nodes.
19856      * @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
19857      * @param {Boolean} keepExisting (optional) true to keep existing selections
19858      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19859      */
19860     select : function(nodeInfo, keepExisting, suppressEvent){
19861         if(nodeInfo instanceof Array){
19862             if(!keepExisting){
19863                 this.clearSelections(true);
19864             }
19865             for(var i = 0, len = nodeInfo.length; i < len; i++){
19866                 this.select(nodeInfo[i], true, true);
19867             }
19868             return;
19869         } 
19870         var node = this.getNode(nodeInfo);
19871         if(!node || this.isSelected(node)){
19872             return; // already selected.
19873         }
19874         if(!keepExisting){
19875             this.clearSelections(true);
19876         }
19877         
19878         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19879             Roo.fly(node).addClass(this.selectedClass);
19880             this.selections.push(node);
19881             if(!suppressEvent){
19882                 this.fireEvent("selectionchange", this, this.selections);
19883             }
19884         }
19885         
19886         
19887     },
19888       /**
19889      * Unselects nodes.
19890      * @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
19891      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19892      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19893      */
19894     unselect : function(nodeInfo, keepExisting, suppressEvent)
19895     {
19896         if(nodeInfo instanceof Array){
19897             Roo.each(this.selections, function(s) {
19898                 this.unselect(s, nodeInfo);
19899             }, this);
19900             return;
19901         }
19902         var node = this.getNode(nodeInfo);
19903         if(!node || !this.isSelected(node)){
19904             //Roo.log("not selected");
19905             return; // not selected.
19906         }
19907         // fireevent???
19908         var ns = [];
19909         Roo.each(this.selections, function(s) {
19910             if (s == node ) {
19911                 Roo.fly(node).removeClass(this.selectedClass);
19912
19913                 return;
19914             }
19915             ns.push(s);
19916         },this);
19917         
19918         this.selections= ns;
19919         this.fireEvent("selectionchange", this, this.selections);
19920     },
19921
19922     /**
19923      * Gets a template node.
19924      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19925      * @return {HTMLElement} The node or null if it wasn't found
19926      */
19927     getNode : function(nodeInfo){
19928         if(typeof nodeInfo == "string"){
19929             return document.getElementById(nodeInfo);
19930         }else if(typeof nodeInfo == "number"){
19931             return this.nodes[nodeInfo];
19932         }
19933         return nodeInfo;
19934     },
19935
19936     /**
19937      * Gets a range template nodes.
19938      * @param {Number} startIndex
19939      * @param {Number} endIndex
19940      * @return {Array} An array of nodes
19941      */
19942     getNodes : function(start, end){
19943         var ns = this.nodes;
19944         start = start || 0;
19945         end = typeof end == "undefined" ? ns.length - 1 : end;
19946         var nodes = [];
19947         if(start <= end){
19948             for(var i = start; i <= end; i++){
19949                 nodes.push(ns[i]);
19950             }
19951         } else{
19952             for(var i = start; i >= end; i--){
19953                 nodes.push(ns[i]);
19954             }
19955         }
19956         return nodes;
19957     },
19958
19959     /**
19960      * Finds the index of the passed node
19961      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19962      * @return {Number} The index of the node or -1
19963      */
19964     indexOf : function(node){
19965         node = this.getNode(node);
19966         if(typeof node.nodeIndex == "number"){
19967             return node.nodeIndex;
19968         }
19969         var ns = this.nodes;
19970         for(var i = 0, len = ns.length; i < len; i++){
19971             if(ns[i] == node){
19972                 return i;
19973             }
19974         }
19975         return -1;
19976     }
19977 });
19978 /*
19979  * - LGPL
19980  *
19981  * based on jquery fullcalendar
19982  * 
19983  */
19984
19985 Roo.bootstrap = Roo.bootstrap || {};
19986 /**
19987  * @class Roo.bootstrap.Calendar
19988  * @extends Roo.bootstrap.Component
19989  * Bootstrap Calendar class
19990  * @cfg {Boolean} loadMask (true|false) default false
19991  * @cfg {Object} header generate the user specific header of the calendar, default false
19992
19993  * @constructor
19994  * Create a new Container
19995  * @param {Object} config The config object
19996  */
19997
19998
19999
20000 Roo.bootstrap.Calendar = function(config){
20001     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20002      this.addEvents({
20003         /**
20004              * @event select
20005              * Fires when a date is selected
20006              * @param {DatePicker} this
20007              * @param {Date} date The selected date
20008              */
20009         'select': true,
20010         /**
20011              * @event monthchange
20012              * Fires when the displayed month changes 
20013              * @param {DatePicker} this
20014              * @param {Date} date The selected month
20015              */
20016         'monthchange': true,
20017         /**
20018              * @event evententer
20019              * Fires when mouse over an event
20020              * @param {Calendar} this
20021              * @param {event} Event
20022              */
20023         'evententer': true,
20024         /**
20025              * @event eventleave
20026              * Fires when the mouse leaves an
20027              * @param {Calendar} this
20028              * @param {event}
20029              */
20030         'eventleave': true,
20031         /**
20032              * @event eventclick
20033              * Fires when the mouse click an
20034              * @param {Calendar} this
20035              * @param {event}
20036              */
20037         'eventclick': true
20038         
20039     });
20040
20041 };
20042
20043 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20044     
20045      /**
20046      * @cfg {Number} startDay
20047      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20048      */
20049     startDay : 0,
20050     
20051     loadMask : false,
20052     
20053     header : false,
20054       
20055     getAutoCreate : function(){
20056         
20057         
20058         var fc_button = function(name, corner, style, content ) {
20059             return Roo.apply({},{
20060                 tag : 'span',
20061                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20062                          (corner.length ?
20063                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20064                             ''
20065                         ),
20066                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20067                 unselectable: 'on'
20068             });
20069         };
20070         
20071         var header = {};
20072         
20073         if(!this.header){
20074             header = {
20075                 tag : 'table',
20076                 cls : 'fc-header',
20077                 style : 'width:100%',
20078                 cn : [
20079                     {
20080                         tag: 'tr',
20081                         cn : [
20082                             {
20083                                 tag : 'td',
20084                                 cls : 'fc-header-left',
20085                                 cn : [
20086                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20087                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20088                                     { tag: 'span', cls: 'fc-header-space' },
20089                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20090
20091
20092                                 ]
20093                             },
20094
20095                             {
20096                                 tag : 'td',
20097                                 cls : 'fc-header-center',
20098                                 cn : [
20099                                     {
20100                                         tag: 'span',
20101                                         cls: 'fc-header-title',
20102                                         cn : {
20103                                             tag: 'H2',
20104                                             html : 'month / year'
20105                                         }
20106                                     }
20107
20108                                 ]
20109                             },
20110                             {
20111                                 tag : 'td',
20112                                 cls : 'fc-header-right',
20113                                 cn : [
20114                               /*      fc_button('month', 'left', '', 'month' ),
20115                                     fc_button('week', '', '', 'week' ),
20116                                     fc_button('day', 'right', '', 'day' )
20117                                 */    
20118
20119                                 ]
20120                             }
20121
20122                         ]
20123                     }
20124                 ]
20125             };
20126         }
20127         
20128         header = this.header;
20129         
20130        
20131         var cal_heads = function() {
20132             var ret = [];
20133             // fixme - handle this.
20134             
20135             for (var i =0; i < Date.dayNames.length; i++) {
20136                 var d = Date.dayNames[i];
20137                 ret.push({
20138                     tag: 'th',
20139                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20140                     html : d.substring(0,3)
20141                 });
20142                 
20143             }
20144             ret[0].cls += ' fc-first';
20145             ret[6].cls += ' fc-last';
20146             return ret;
20147         };
20148         var cal_cell = function(n) {
20149             return  {
20150                 tag: 'td',
20151                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20152                 cn : [
20153                     {
20154                         cn : [
20155                             {
20156                                 cls: 'fc-day-number',
20157                                 html: 'D'
20158                             },
20159                             {
20160                                 cls: 'fc-day-content',
20161                              
20162                                 cn : [
20163                                      {
20164                                         style: 'position: relative;' // height: 17px;
20165                                     }
20166                                 ]
20167                             }
20168                             
20169                             
20170                         ]
20171                     }
20172                 ]
20173                 
20174             }
20175         };
20176         var cal_rows = function() {
20177             
20178             var ret = [];
20179             for (var r = 0; r < 6; r++) {
20180                 var row= {
20181                     tag : 'tr',
20182                     cls : 'fc-week',
20183                     cn : []
20184                 };
20185                 
20186                 for (var i =0; i < Date.dayNames.length; i++) {
20187                     var d = Date.dayNames[i];
20188                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20189
20190                 }
20191                 row.cn[0].cls+=' fc-first';
20192                 row.cn[0].cn[0].style = 'min-height:90px';
20193                 row.cn[6].cls+=' fc-last';
20194                 ret.push(row);
20195                 
20196             }
20197             ret[0].cls += ' fc-first';
20198             ret[4].cls += ' fc-prev-last';
20199             ret[5].cls += ' fc-last';
20200             return ret;
20201             
20202         };
20203         
20204         var cal_table = {
20205             tag: 'table',
20206             cls: 'fc-border-separate',
20207             style : 'width:100%',
20208             cellspacing  : 0,
20209             cn : [
20210                 { 
20211                     tag: 'thead',
20212                     cn : [
20213                         { 
20214                             tag: 'tr',
20215                             cls : 'fc-first fc-last',
20216                             cn : cal_heads()
20217                         }
20218                     ]
20219                 },
20220                 { 
20221                     tag: 'tbody',
20222                     cn : cal_rows()
20223                 }
20224                   
20225             ]
20226         };
20227          
20228          var cfg = {
20229             cls : 'fc fc-ltr',
20230             cn : [
20231                 header,
20232                 {
20233                     cls : 'fc-content',
20234                     style : "position: relative;",
20235                     cn : [
20236                         {
20237                             cls : 'fc-view fc-view-month fc-grid',
20238                             style : 'position: relative',
20239                             unselectable : 'on',
20240                             cn : [
20241                                 {
20242                                     cls : 'fc-event-container',
20243                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20244                                 },
20245                                 cal_table
20246                             ]
20247                         }
20248                     ]
20249     
20250                 }
20251            ] 
20252             
20253         };
20254         
20255          
20256         
20257         return cfg;
20258     },
20259     
20260     
20261     initEvents : function()
20262     {
20263         if(!this.store){
20264             throw "can not find store for calendar";
20265         }
20266         
20267         var mark = {
20268             tag: "div",
20269             cls:"x-dlg-mask",
20270             style: "text-align:center",
20271             cn: [
20272                 {
20273                     tag: "div",
20274                     style: "background-color:white;width:50%;margin:250 auto",
20275                     cn: [
20276                         {
20277                             tag: "img",
20278                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20279                         },
20280                         {
20281                             tag: "span",
20282                             html: "Loading"
20283                         }
20284                         
20285                     ]
20286                 }
20287             ]
20288         };
20289         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20290         
20291         var size = this.el.select('.fc-content', true).first().getSize();
20292         this.maskEl.setSize(size.width, size.height);
20293         this.maskEl.enableDisplayMode("block");
20294         if(!this.loadMask){
20295             this.maskEl.hide();
20296         }
20297         
20298         this.store = Roo.factory(this.store, Roo.data);
20299         this.store.on('load', this.onLoad, this);
20300         this.store.on('beforeload', this.onBeforeLoad, this);
20301         
20302         this.resize();
20303         
20304         this.cells = this.el.select('.fc-day',true);
20305         //Roo.log(this.cells);
20306         this.textNodes = this.el.query('.fc-day-number');
20307         this.cells.addClassOnOver('fc-state-hover');
20308         
20309         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20310         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20311         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20312         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20313         
20314         this.on('monthchange', this.onMonthChange, this);
20315         
20316         this.update(new Date().clearTime());
20317     },
20318     
20319     resize : function() {
20320         var sz  = this.el.getSize();
20321         
20322         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20323         this.el.select('.fc-day-content div',true).setHeight(34);
20324     },
20325     
20326     
20327     // private
20328     showPrevMonth : function(e){
20329         this.update(this.activeDate.add("mo", -1));
20330     },
20331     showToday : function(e){
20332         this.update(new Date().clearTime());
20333     },
20334     // private
20335     showNextMonth : function(e){
20336         this.update(this.activeDate.add("mo", 1));
20337     },
20338
20339     // private
20340     showPrevYear : function(){
20341         this.update(this.activeDate.add("y", -1));
20342     },
20343
20344     // private
20345     showNextYear : function(){
20346         this.update(this.activeDate.add("y", 1));
20347     },
20348
20349     
20350    // private
20351     update : function(date)
20352     {
20353         var vd = this.activeDate;
20354         this.activeDate = date;
20355 //        if(vd && this.el){
20356 //            var t = date.getTime();
20357 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20358 //                Roo.log('using add remove');
20359 //                
20360 //                this.fireEvent('monthchange', this, date);
20361 //                
20362 //                this.cells.removeClass("fc-state-highlight");
20363 //                this.cells.each(function(c){
20364 //                   if(c.dateValue == t){
20365 //                       c.addClass("fc-state-highlight");
20366 //                       setTimeout(function(){
20367 //                            try{c.dom.firstChild.focus();}catch(e){}
20368 //                       }, 50);
20369 //                       return false;
20370 //                   }
20371 //                   return true;
20372 //                });
20373 //                return;
20374 //            }
20375 //        }
20376         
20377         var days = date.getDaysInMonth();
20378         
20379         var firstOfMonth = date.getFirstDateOfMonth();
20380         var startingPos = firstOfMonth.getDay()-this.startDay;
20381         
20382         if(startingPos < this.startDay){
20383             startingPos += 7;
20384         }
20385         
20386         var pm = date.add(Date.MONTH, -1);
20387         var prevStart = pm.getDaysInMonth()-startingPos;
20388 //        
20389         this.cells = this.el.select('.fc-day',true);
20390         this.textNodes = this.el.query('.fc-day-number');
20391         this.cells.addClassOnOver('fc-state-hover');
20392         
20393         var cells = this.cells.elements;
20394         var textEls = this.textNodes;
20395         
20396         Roo.each(cells, function(cell){
20397             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20398         });
20399         
20400         days += startingPos;
20401
20402         // convert everything to numbers so it's fast
20403         var day = 86400000;
20404         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20405         //Roo.log(d);
20406         //Roo.log(pm);
20407         //Roo.log(prevStart);
20408         
20409         var today = new Date().clearTime().getTime();
20410         var sel = date.clearTime().getTime();
20411         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20412         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20413         var ddMatch = this.disabledDatesRE;
20414         var ddText = this.disabledDatesText;
20415         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20416         var ddaysText = this.disabledDaysText;
20417         var format = this.format;
20418         
20419         var setCellClass = function(cal, cell){
20420             cell.row = 0;
20421             cell.events = [];
20422             cell.more = [];
20423             //Roo.log('set Cell Class');
20424             cell.title = "";
20425             var t = d.getTime();
20426             
20427             //Roo.log(d);
20428             
20429             cell.dateValue = t;
20430             if(t == today){
20431                 cell.className += " fc-today";
20432                 cell.className += " fc-state-highlight";
20433                 cell.title = cal.todayText;
20434             }
20435             if(t == sel){
20436                 // disable highlight in other month..
20437                 //cell.className += " fc-state-highlight";
20438                 
20439             }
20440             // disabling
20441             if(t < min) {
20442                 cell.className = " fc-state-disabled";
20443                 cell.title = cal.minText;
20444                 return;
20445             }
20446             if(t > max) {
20447                 cell.className = " fc-state-disabled";
20448                 cell.title = cal.maxText;
20449                 return;
20450             }
20451             if(ddays){
20452                 if(ddays.indexOf(d.getDay()) != -1){
20453                     cell.title = ddaysText;
20454                     cell.className = " fc-state-disabled";
20455                 }
20456             }
20457             if(ddMatch && format){
20458                 var fvalue = d.dateFormat(format);
20459                 if(ddMatch.test(fvalue)){
20460                     cell.title = ddText.replace("%0", fvalue);
20461                     cell.className = " fc-state-disabled";
20462                 }
20463             }
20464             
20465             if (!cell.initialClassName) {
20466                 cell.initialClassName = cell.dom.className;
20467             }
20468             
20469             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20470         };
20471
20472         var i = 0;
20473         
20474         for(; i < startingPos; i++) {
20475             textEls[i].innerHTML = (++prevStart);
20476             d.setDate(d.getDate()+1);
20477             
20478             cells[i].className = "fc-past fc-other-month";
20479             setCellClass(this, cells[i]);
20480         }
20481         
20482         var intDay = 0;
20483         
20484         for(; i < days; i++){
20485             intDay = i - startingPos + 1;
20486             textEls[i].innerHTML = (intDay);
20487             d.setDate(d.getDate()+1);
20488             
20489             cells[i].className = ''; // "x-date-active";
20490             setCellClass(this, cells[i]);
20491         }
20492         var extraDays = 0;
20493         
20494         for(; i < 42; i++) {
20495             textEls[i].innerHTML = (++extraDays);
20496             d.setDate(d.getDate()+1);
20497             
20498             cells[i].className = "fc-future fc-other-month";
20499             setCellClass(this, cells[i]);
20500         }
20501         
20502         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20503         
20504         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20505         
20506         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20507         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20508         
20509         if(totalRows != 6){
20510             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20511             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20512         }
20513         
20514         this.fireEvent('monthchange', this, date);
20515         
20516         
20517         /*
20518         if(!this.internalRender){
20519             var main = this.el.dom.firstChild;
20520             var w = main.offsetWidth;
20521             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20522             Roo.fly(main).setWidth(w);
20523             this.internalRender = true;
20524             // opera does not respect the auto grow header center column
20525             // then, after it gets a width opera refuses to recalculate
20526             // without a second pass
20527             if(Roo.isOpera && !this.secondPass){
20528                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20529                 this.secondPass = true;
20530                 this.update.defer(10, this, [date]);
20531             }
20532         }
20533         */
20534         
20535     },
20536     
20537     findCell : function(dt) {
20538         dt = dt.clearTime().getTime();
20539         var ret = false;
20540         this.cells.each(function(c){
20541             //Roo.log("check " +c.dateValue + '?=' + dt);
20542             if(c.dateValue == dt){
20543                 ret = c;
20544                 return false;
20545             }
20546             return true;
20547         });
20548         
20549         return ret;
20550     },
20551     
20552     findCells : function(ev) {
20553         var s = ev.start.clone().clearTime().getTime();
20554        // Roo.log(s);
20555         var e= ev.end.clone().clearTime().getTime();
20556        // Roo.log(e);
20557         var ret = [];
20558         this.cells.each(function(c){
20559              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20560             
20561             if(c.dateValue > e){
20562                 return ;
20563             }
20564             if(c.dateValue < s){
20565                 return ;
20566             }
20567             ret.push(c);
20568         });
20569         
20570         return ret;    
20571     },
20572     
20573 //    findBestRow: function(cells)
20574 //    {
20575 //        var ret = 0;
20576 //        
20577 //        for (var i =0 ; i < cells.length;i++) {
20578 //            ret  = Math.max(cells[i].rows || 0,ret);
20579 //        }
20580 //        return ret;
20581 //        
20582 //    },
20583     
20584     
20585     addItem : function(ev)
20586     {
20587         // look for vertical location slot in
20588         var cells = this.findCells(ev);
20589         
20590 //        ev.row = this.findBestRow(cells);
20591         
20592         // work out the location.
20593         
20594         var crow = false;
20595         var rows = [];
20596         for(var i =0; i < cells.length; i++) {
20597             
20598             cells[i].row = cells[0].row;
20599             
20600             if(i == 0){
20601                 cells[i].row = cells[i].row + 1;
20602             }
20603             
20604             if (!crow) {
20605                 crow = {
20606                     start : cells[i],
20607                     end :  cells[i]
20608                 };
20609                 continue;
20610             }
20611             if (crow.start.getY() == cells[i].getY()) {
20612                 // on same row.
20613                 crow.end = cells[i];
20614                 continue;
20615             }
20616             // different row.
20617             rows.push(crow);
20618             crow = {
20619                 start: cells[i],
20620                 end : cells[i]
20621             };
20622             
20623         }
20624         
20625         rows.push(crow);
20626         ev.els = [];
20627         ev.rows = rows;
20628         ev.cells = cells;
20629         
20630         cells[0].events.push(ev);
20631         
20632         this.calevents.push(ev);
20633     },
20634     
20635     clearEvents: function() {
20636         
20637         if(!this.calevents){
20638             return;
20639         }
20640         
20641         Roo.each(this.cells.elements, function(c){
20642             c.row = 0;
20643             c.events = [];
20644             c.more = [];
20645         });
20646         
20647         Roo.each(this.calevents, function(e) {
20648             Roo.each(e.els, function(el) {
20649                 el.un('mouseenter' ,this.onEventEnter, this);
20650                 el.un('mouseleave' ,this.onEventLeave, this);
20651                 el.remove();
20652             },this);
20653         },this);
20654         
20655         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20656             e.remove();
20657         });
20658         
20659     },
20660     
20661     renderEvents: function()
20662     {   
20663         var _this = this;
20664         
20665         this.cells.each(function(c) {
20666             
20667             if(c.row < 5){
20668                 return;
20669             }
20670             
20671             var ev = c.events;
20672             
20673             var r = 4;
20674             if(c.row != c.events.length){
20675                 r = 4 - (4 - (c.row - c.events.length));
20676             }
20677             
20678             c.events = ev.slice(0, r);
20679             c.more = ev.slice(r);
20680             
20681             if(c.more.length && c.more.length == 1){
20682                 c.events.push(c.more.pop());
20683             }
20684             
20685             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20686             
20687         });
20688             
20689         this.cells.each(function(c) {
20690             
20691             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20692             
20693             
20694             for (var e = 0; e < c.events.length; e++){
20695                 var ev = c.events[e];
20696                 var rows = ev.rows;
20697                 
20698                 for(var i = 0; i < rows.length; i++) {
20699                 
20700                     // how many rows should it span..
20701
20702                     var  cfg = {
20703                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20704                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20705
20706                         unselectable : "on",
20707                         cn : [
20708                             {
20709                                 cls: 'fc-event-inner',
20710                                 cn : [
20711     //                                {
20712     //                                  tag:'span',
20713     //                                  cls: 'fc-event-time',
20714     //                                  html : cells.length > 1 ? '' : ev.time
20715     //                                },
20716                                     {
20717                                       tag:'span',
20718                                       cls: 'fc-event-title',
20719                                       html : String.format('{0}', ev.title)
20720                                     }
20721
20722
20723                                 ]
20724                             },
20725                             {
20726                                 cls: 'ui-resizable-handle ui-resizable-e',
20727                                 html : '&nbsp;&nbsp;&nbsp'
20728                             }
20729
20730                         ]
20731                     };
20732
20733                     if (i == 0) {
20734                         cfg.cls += ' fc-event-start';
20735                     }
20736                     if ((i+1) == rows.length) {
20737                         cfg.cls += ' fc-event-end';
20738                     }
20739
20740                     var ctr = _this.el.select('.fc-event-container',true).first();
20741                     var cg = ctr.createChild(cfg);
20742
20743                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20744                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20745
20746                     var r = (c.more.length) ? 1 : 0;
20747                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20748                     cg.setWidth(ebox.right - sbox.x -2);
20749
20750                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20751                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20752                     cg.on('click', _this.onEventClick, _this, ev);
20753
20754                     ev.els.push(cg);
20755                     
20756                 }
20757                 
20758             }
20759             
20760             
20761             if(c.more.length){
20762                 var  cfg = {
20763                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20764                     style : 'position: absolute',
20765                     unselectable : "on",
20766                     cn : [
20767                         {
20768                             cls: 'fc-event-inner',
20769                             cn : [
20770                                 {
20771                                   tag:'span',
20772                                   cls: 'fc-event-title',
20773                                   html : 'More'
20774                                 }
20775
20776
20777                             ]
20778                         },
20779                         {
20780                             cls: 'ui-resizable-handle ui-resizable-e',
20781                             html : '&nbsp;&nbsp;&nbsp'
20782                         }
20783
20784                     ]
20785                 };
20786
20787                 var ctr = _this.el.select('.fc-event-container',true).first();
20788                 var cg = ctr.createChild(cfg);
20789
20790                 var sbox = c.select('.fc-day-content',true).first().getBox();
20791                 var ebox = c.select('.fc-day-content',true).first().getBox();
20792                 //Roo.log(cg);
20793                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20794                 cg.setWidth(ebox.right - sbox.x -2);
20795
20796                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20797                 
20798             }
20799             
20800         });
20801         
20802         
20803         
20804     },
20805     
20806     onEventEnter: function (e, el,event,d) {
20807         this.fireEvent('evententer', this, el, event);
20808     },
20809     
20810     onEventLeave: function (e, el,event,d) {
20811         this.fireEvent('eventleave', this, el, event);
20812     },
20813     
20814     onEventClick: function (e, el,event,d) {
20815         this.fireEvent('eventclick', this, el, event);
20816     },
20817     
20818     onMonthChange: function () {
20819         this.store.load();
20820     },
20821     
20822     onMoreEventClick: function(e, el, more)
20823     {
20824         var _this = this;
20825         
20826         this.calpopover.placement = 'right';
20827         this.calpopover.setTitle('More');
20828         
20829         this.calpopover.setContent('');
20830         
20831         var ctr = this.calpopover.el.select('.popover-content', true).first();
20832         
20833         Roo.each(more, function(m){
20834             var cfg = {
20835                 cls : 'fc-event-hori fc-event-draggable',
20836                 html : m.title
20837             };
20838             var cg = ctr.createChild(cfg);
20839             
20840             cg.on('click', _this.onEventClick, _this, m);
20841         });
20842         
20843         this.calpopover.show(el);
20844         
20845         
20846     },
20847     
20848     onLoad: function () 
20849     {   
20850         this.calevents = [];
20851         var cal = this;
20852         
20853         if(this.store.getCount() > 0){
20854             this.store.data.each(function(d){
20855                cal.addItem({
20856                     id : d.data.id,
20857                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20858                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20859                     time : d.data.start_time,
20860                     title : d.data.title,
20861                     description : d.data.description,
20862                     venue : d.data.venue
20863                 });
20864             });
20865         }
20866         
20867         this.renderEvents();
20868         
20869         if(this.calevents.length && this.loadMask){
20870             this.maskEl.hide();
20871         }
20872     },
20873     
20874     onBeforeLoad: function()
20875     {
20876         this.clearEvents();
20877         if(this.loadMask){
20878             this.maskEl.show();
20879         }
20880     }
20881 });
20882
20883  
20884  /*
20885  * - LGPL
20886  *
20887  * element
20888  * 
20889  */
20890
20891 /**
20892  * @class Roo.bootstrap.Popover
20893  * @extends Roo.bootstrap.Component
20894  * Bootstrap Popover class
20895  * @cfg {String} html contents of the popover   (or false to use children..)
20896  * @cfg {String} title of popover (or false to hide)
20897  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20898  * @cfg {String} trigger click || hover (or false to trigger manually)
20899  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20900  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20901  *      - if false and it has a 'parent' then it will be automatically added to that element
20902  *      - if string - Roo.get  will be called 
20903  * @cfg {Number} delay - delay before showing
20904  
20905  * @constructor
20906  * Create a new Popover
20907  * @param {Object} config The config object
20908  */
20909
20910 Roo.bootstrap.Popover = function(config){
20911     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20912     
20913     this.addEvents({
20914         // raw events
20915          /**
20916          * @event show
20917          * After the popover show
20918          * 
20919          * @param {Roo.bootstrap.Popover} this
20920          */
20921         "show" : true,
20922         /**
20923          * @event hide
20924          * After the popover hide
20925          * 
20926          * @param {Roo.bootstrap.Popover} this
20927          */
20928         "hide" : true
20929     });
20930 };
20931
20932 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20933     
20934     title: false,
20935     html: false,
20936     
20937     placement : 'right',
20938     trigger : 'hover', // hover
20939     modal : false,
20940     delay : 0,
20941     
20942     over: false,
20943     
20944     can_build_overlaid : false,
20945     
20946     maskEl : false, // the mask element
20947     headerEl : false,
20948     contentEl : false,
20949     alignEl : false, // when show is called with an element - this get's stored.
20950     
20951     getChildContainer : function()
20952     {
20953         return this.contentEl;
20954         
20955     },
20956     getPopoverHeader : function()
20957     {
20958         this.title = true; // flag not to hide it..
20959         this.headerEl.addClass('p-0');
20960         return this.headerEl
20961     },
20962     
20963     
20964     getAutoCreate : function(){
20965          
20966         var cfg = {
20967            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20968            style: 'display:block',
20969            cn : [
20970                 {
20971                     cls : 'arrow'
20972                 },
20973                 {
20974                     cls : 'popover-inner ',
20975                     cn : [
20976                         {
20977                             tag: 'h3',
20978                             cls: 'popover-title popover-header',
20979                             html : this.title === false ? '' : this.title
20980                         },
20981                         {
20982                             cls : 'popover-content popover-body '  + (this.cls || ''),
20983                             html : this.html || ''
20984                         }
20985                     ]
20986                     
20987                 }
20988            ]
20989         };
20990         
20991         return cfg;
20992     },
20993     /**
20994      * @param {string} the title
20995      */
20996     setTitle: function(str)
20997     {
20998         this.title = str;
20999         if (this.el) {
21000             this.headerEl.dom.innerHTML = str;
21001         }
21002         
21003     },
21004     /**
21005      * @param {string} the body content
21006      */
21007     setContent: function(str)
21008     {
21009         this.html = str;
21010         if (this.contentEl) {
21011             this.contentEl.dom.innerHTML = str;
21012         }
21013         
21014     },
21015     // as it get's added to the bottom of the page.
21016     onRender : function(ct, position)
21017     {
21018         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21019         
21020         
21021         
21022         if(!this.el){
21023             var cfg = Roo.apply({},  this.getAutoCreate());
21024             cfg.id = Roo.id();
21025             
21026             if (this.cls) {
21027                 cfg.cls += ' ' + this.cls;
21028             }
21029             if (this.style) {
21030                 cfg.style = this.style;
21031             }
21032             //Roo.log("adding to ");
21033             this.el = Roo.get(document.body).createChild(cfg, position);
21034 //            Roo.log(this.el);
21035         }
21036         
21037         this.contentEl = this.el.select('.popover-content',true).first();
21038         this.headerEl =  this.el.select('.popover-title',true).first();
21039         
21040         var nitems = [];
21041         if(typeof(this.items) != 'undefined'){
21042             var items = this.items;
21043             delete this.items;
21044
21045             for(var i =0;i < items.length;i++) {
21046                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21047             }
21048         }
21049
21050         this.items = nitems;
21051         
21052         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21053         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21054         
21055         
21056         
21057         this.initEvents();
21058     },
21059     
21060     resizeMask : function()
21061     {
21062         this.maskEl.setSize(
21063             Roo.lib.Dom.getViewWidth(true),
21064             Roo.lib.Dom.getViewHeight(true)
21065         );
21066     },
21067     
21068     initEvents : function()
21069     {
21070         
21071         if (!this.modal) { 
21072             Roo.bootstrap.Popover.register(this);
21073         }
21074          
21075         this.arrowEl = this.el.select('.arrow',true).first();
21076         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21077         this.el.enableDisplayMode('block');
21078         this.el.hide();
21079  
21080         
21081         if (this.over === false && !this.parent()) {
21082             return; 
21083         }
21084         if (this.triggers === false) {
21085             return;
21086         }
21087          
21088         // support parent
21089         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21090         var triggers = this.trigger ? this.trigger.split(' ') : [];
21091         Roo.each(triggers, function(trigger) {
21092         
21093             if (trigger == 'click') {
21094                 on_el.on('click', this.toggle, this);
21095             } else if (trigger != 'manual') {
21096                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21097                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21098       
21099                 on_el.on(eventIn  ,this.enter, this);
21100                 on_el.on(eventOut, this.leave, this);
21101             }
21102         }, this);
21103     },
21104     
21105     
21106     // private
21107     timeout : null,
21108     hoverState : null,
21109     
21110     toggle : function () {
21111         this.hoverState == 'in' ? this.leave() : this.enter();
21112     },
21113     
21114     enter : function () {
21115         
21116         clearTimeout(this.timeout);
21117     
21118         this.hoverState = 'in';
21119     
21120         if (!this.delay || !this.delay.show) {
21121             this.show();
21122             return;
21123         }
21124         var _t = this;
21125         this.timeout = setTimeout(function () {
21126             if (_t.hoverState == 'in') {
21127                 _t.show();
21128             }
21129         }, this.delay.show)
21130     },
21131     
21132     leave : function() {
21133         clearTimeout(this.timeout);
21134     
21135         this.hoverState = 'out';
21136     
21137         if (!this.delay || !this.delay.hide) {
21138             this.hide();
21139             return;
21140         }
21141         var _t = this;
21142         this.timeout = setTimeout(function () {
21143             if (_t.hoverState == 'out') {
21144                 _t.hide();
21145             }
21146         }, this.delay.hide)
21147     },
21148     /**
21149      * Show the popover
21150      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21151      * @param {string} (left|right|top|bottom) position
21152      */
21153     show : function (on_el, placement)
21154     {
21155         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21156         on_el = on_el || false; // default to false
21157          
21158         if (!on_el) {
21159             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21160                 on_el = this.parent().el;
21161             } else if (this.over) {
21162                 on_el = Roo.get(this.over);
21163             }
21164             
21165         }
21166         
21167         this.alignEl = Roo.get( on_el );
21168
21169         if (!this.el) {
21170             this.render(document.body);
21171         }
21172         
21173         
21174          
21175         
21176         if (this.title === false) {
21177             this.headerEl.hide();
21178         }
21179         
21180        
21181         this.el.show();
21182         this.el.dom.style.display = 'block';
21183          
21184  
21185         if (this.alignEl) {
21186             this.updatePosition(this.placement, true);
21187              
21188         } else {
21189             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21190             var es = this.el.getSize();
21191             var x = Roo.lib.Dom.getViewWidth()/2;
21192             var y = Roo.lib.Dom.getViewHeight()/2;
21193             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21194             
21195         }
21196
21197         
21198         //var arrow = this.el.select('.arrow',true).first();
21199         //arrow.set(align[2], 
21200         
21201         this.el.addClass('in');
21202         
21203          
21204         
21205         this.hoverState = 'in';
21206         
21207         if (this.modal) {
21208             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21209             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21210             this.maskEl.dom.style.display = 'block';
21211             this.maskEl.addClass('show');
21212         }
21213         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21214  
21215         this.fireEvent('show', this);
21216         
21217     },
21218     /**
21219      * fire this manually after loading a grid in the table for example
21220      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21221      * @param {Boolean} try and move it if we cant get right position.
21222      */
21223     updatePosition : function(placement, try_move)
21224     {
21225         // allow for calling with no parameters
21226         placement = placement   ? placement :  this.placement;
21227         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21228         
21229         this.el.removeClass([
21230             'fade','top','bottom', 'left', 'right','in',
21231             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21232         ]);
21233         this.el.addClass(placement + ' bs-popover-' + placement);
21234         
21235         if (!this.alignEl ) {
21236             return false;
21237         }
21238         
21239         switch (placement) {
21240             case 'right':
21241                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21242                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21243                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21244                     //normal display... or moved up/down.
21245                     this.el.setXY(offset);
21246                     var xy = this.alignEl.getAnchorXY('tr', false);
21247                     xy[0]+=2;xy[1]+=5;
21248                     this.arrowEl.setXY(xy);
21249                     return true;
21250                 }
21251                 // continue through...
21252                 return this.updatePosition('left', false);
21253                 
21254             
21255             case 'left':
21256                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21257                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21258                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21259                     //normal display... or moved up/down.
21260                     this.el.setXY(offset);
21261                     var xy = this.alignEl.getAnchorXY('tl', false);
21262                     xy[0]-=10;xy[1]+=5; // << fix me
21263                     this.arrowEl.setXY(xy);
21264                     return true;
21265                 }
21266                 // call self...
21267                 return this.updatePosition('right', false);
21268             
21269             case 'top':
21270                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21271                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21272                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21273                     //normal display... or moved up/down.
21274                     this.el.setXY(offset);
21275                     var xy = this.alignEl.getAnchorXY('t', false);
21276                     xy[1]-=10; // << fix me
21277                     this.arrowEl.setXY(xy);
21278                     return true;
21279                 }
21280                 // fall through
21281                return this.updatePosition('bottom', false);
21282             
21283             case 'bottom':
21284                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21285                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21286                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21287                     //normal display... or moved up/down.
21288                     this.el.setXY(offset);
21289                     var xy = this.alignEl.getAnchorXY('b', false);
21290                      xy[1]+=2; // << fix me
21291                     this.arrowEl.setXY(xy);
21292                     return true;
21293                 }
21294                 // fall through
21295                 return this.updatePosition('top', false);
21296                 
21297             
21298         }
21299         
21300         
21301         return false;
21302     },
21303     
21304     hide : function()
21305     {
21306         this.el.setXY([0,0]);
21307         this.el.removeClass('in');
21308         this.el.hide();
21309         this.hoverState = null;
21310         this.maskEl.hide(); // always..
21311         this.fireEvent('hide', this);
21312     }
21313     
21314 });
21315
21316
21317 Roo.apply(Roo.bootstrap.Popover, {
21318
21319     alignment : {
21320         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21321         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21322         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21323         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21324     },
21325     
21326     zIndex : 20001,
21327
21328     clickHander : false,
21329     
21330     
21331
21332     onMouseDown : function(e)
21333     {
21334         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21335             /// what is nothing is showing..
21336             this.hideAll();
21337         }
21338          
21339     },
21340     
21341     
21342     popups : [],
21343     
21344     register : function(popup)
21345     {
21346         if (!Roo.bootstrap.Popover.clickHandler) {
21347             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21348         }
21349         // hide other popups.
21350         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21351         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21352         this.hideAll(); //<< why?
21353         //this.popups.push(popup);
21354     },
21355     hideAll : function()
21356     {
21357         this.popups.forEach(function(p) {
21358             p.hide();
21359         });
21360     },
21361     onShow : function() {
21362         Roo.bootstrap.Popover.popups.push(this);
21363     },
21364     onHide : function() {
21365         Roo.bootstrap.Popover.popups.remove(this);
21366     } 
21367
21368 });/*
21369  * - LGPL
21370  *
21371  * Card header - holder for the card header elements.
21372  * 
21373  */
21374
21375 /**
21376  * @class Roo.bootstrap.PopoverNav
21377  * @extends Roo.bootstrap.NavGroup
21378  * Bootstrap Popover header navigation class
21379  * @constructor
21380  * Create a new Popover Header Navigation 
21381  * @param {Object} config The config object
21382  */
21383
21384 Roo.bootstrap.PopoverNav = function(config){
21385     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21386 };
21387
21388 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21389     
21390     
21391     container_method : 'getPopoverHeader' 
21392     
21393      
21394     
21395     
21396    
21397 });
21398
21399  
21400
21401  /*
21402  * - LGPL
21403  *
21404  * Progress
21405  * 
21406  */
21407
21408 /**
21409  * @class Roo.bootstrap.Progress
21410  * @extends Roo.bootstrap.Component
21411  * Bootstrap Progress class
21412  * @cfg {Boolean} striped striped of the progress bar
21413  * @cfg {Boolean} active animated of the progress bar
21414  * 
21415  * 
21416  * @constructor
21417  * Create a new Progress
21418  * @param {Object} config The config object
21419  */
21420
21421 Roo.bootstrap.Progress = function(config){
21422     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21423 };
21424
21425 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21426     
21427     striped : false,
21428     active: false,
21429     
21430     getAutoCreate : function(){
21431         var cfg = {
21432             tag: 'div',
21433             cls: 'progress'
21434         };
21435         
21436         
21437         if(this.striped){
21438             cfg.cls += ' progress-striped';
21439         }
21440       
21441         if(this.active){
21442             cfg.cls += ' active';
21443         }
21444         
21445         
21446         return cfg;
21447     }
21448    
21449 });
21450
21451  
21452
21453  /*
21454  * - LGPL
21455  *
21456  * ProgressBar
21457  * 
21458  */
21459
21460 /**
21461  * @class Roo.bootstrap.ProgressBar
21462  * @extends Roo.bootstrap.Component
21463  * Bootstrap ProgressBar class
21464  * @cfg {Number} aria_valuenow aria-value now
21465  * @cfg {Number} aria_valuemin aria-value min
21466  * @cfg {Number} aria_valuemax aria-value max
21467  * @cfg {String} label label for the progress bar
21468  * @cfg {String} panel (success | info | warning | danger )
21469  * @cfg {String} role role of the progress bar
21470  * @cfg {String} sr_only text
21471  * 
21472  * 
21473  * @constructor
21474  * Create a new ProgressBar
21475  * @param {Object} config The config object
21476  */
21477
21478 Roo.bootstrap.ProgressBar = function(config){
21479     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21480 };
21481
21482 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21483     
21484     aria_valuenow : 0,
21485     aria_valuemin : 0,
21486     aria_valuemax : 100,
21487     label : false,
21488     panel : false,
21489     role : false,
21490     sr_only: false,
21491     
21492     getAutoCreate : function()
21493     {
21494         
21495         var cfg = {
21496             tag: 'div',
21497             cls: 'progress-bar',
21498             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21499         };
21500         
21501         if(this.sr_only){
21502             cfg.cn = {
21503                 tag: 'span',
21504                 cls: 'sr-only',
21505                 html: this.sr_only
21506             }
21507         }
21508         
21509         if(this.role){
21510             cfg.role = this.role;
21511         }
21512         
21513         if(this.aria_valuenow){
21514             cfg['aria-valuenow'] = this.aria_valuenow;
21515         }
21516         
21517         if(this.aria_valuemin){
21518             cfg['aria-valuemin'] = this.aria_valuemin;
21519         }
21520         
21521         if(this.aria_valuemax){
21522             cfg['aria-valuemax'] = this.aria_valuemax;
21523         }
21524         
21525         if(this.label && !this.sr_only){
21526             cfg.html = this.label;
21527         }
21528         
21529         if(this.panel){
21530             cfg.cls += ' progress-bar-' + this.panel;
21531         }
21532         
21533         return cfg;
21534     },
21535     
21536     update : function(aria_valuenow)
21537     {
21538         this.aria_valuenow = aria_valuenow;
21539         
21540         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21541     }
21542    
21543 });
21544
21545  
21546
21547  /*
21548  * - LGPL
21549  *
21550  * column
21551  * 
21552  */
21553
21554 /**
21555  * @class Roo.bootstrap.TabGroup
21556  * @extends Roo.bootstrap.Column
21557  * Bootstrap Column class
21558  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21559  * @cfg {Boolean} carousel true to make the group behave like a carousel
21560  * @cfg {Boolean} bullets show bullets for the panels
21561  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21562  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21563  * @cfg {Boolean} showarrow (true|false) show arrow default true
21564  * 
21565  * @constructor
21566  * Create a new TabGroup
21567  * @param {Object} config The config object
21568  */
21569
21570 Roo.bootstrap.TabGroup = function(config){
21571     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21572     if (!this.navId) {
21573         this.navId = Roo.id();
21574     }
21575     this.tabs = [];
21576     Roo.bootstrap.TabGroup.register(this);
21577     
21578 };
21579
21580 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21581     
21582     carousel : false,
21583     transition : false,
21584     bullets : 0,
21585     timer : 0,
21586     autoslide : false,
21587     slideFn : false,
21588     slideOnTouch : false,
21589     showarrow : true,
21590     
21591     getAutoCreate : function()
21592     {
21593         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21594         
21595         cfg.cls += ' tab-content';
21596         
21597         if (this.carousel) {
21598             cfg.cls += ' carousel slide';
21599             
21600             cfg.cn = [{
21601                cls : 'carousel-inner',
21602                cn : []
21603             }];
21604         
21605             if(this.bullets  && !Roo.isTouch){
21606                 
21607                 var bullets = {
21608                     cls : 'carousel-bullets',
21609                     cn : []
21610                 };
21611                
21612                 if(this.bullets_cls){
21613                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21614                 }
21615                 
21616                 bullets.cn.push({
21617                     cls : 'clear'
21618                 });
21619                 
21620                 cfg.cn[0].cn.push(bullets);
21621             }
21622             
21623             if(this.showarrow){
21624                 cfg.cn[0].cn.push({
21625                     tag : 'div',
21626                     class : 'carousel-arrow',
21627                     cn : [
21628                         {
21629                             tag : 'div',
21630                             class : 'carousel-prev',
21631                             cn : [
21632                                 {
21633                                     tag : 'i',
21634                                     class : 'fa fa-chevron-left'
21635                                 }
21636                             ]
21637                         },
21638                         {
21639                             tag : 'div',
21640                             class : 'carousel-next',
21641                             cn : [
21642                                 {
21643                                     tag : 'i',
21644                                     class : 'fa fa-chevron-right'
21645                                 }
21646                             ]
21647                         }
21648                     ]
21649                 });
21650             }
21651             
21652         }
21653         
21654         return cfg;
21655     },
21656     
21657     initEvents:  function()
21658     {
21659 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21660 //            this.el.on("touchstart", this.onTouchStart, this);
21661 //        }
21662         
21663         if(this.autoslide){
21664             var _this = this;
21665             
21666             this.slideFn = window.setInterval(function() {
21667                 _this.showPanelNext();
21668             }, this.timer);
21669         }
21670         
21671         if(this.showarrow){
21672             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21673             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21674         }
21675         
21676         
21677     },
21678     
21679 //    onTouchStart : function(e, el, o)
21680 //    {
21681 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21682 //            return;
21683 //        }
21684 //        
21685 //        this.showPanelNext();
21686 //    },
21687     
21688     
21689     getChildContainer : function()
21690     {
21691         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21692     },
21693     
21694     /**
21695     * register a Navigation item
21696     * @param {Roo.bootstrap.NavItem} the navitem to add
21697     */
21698     register : function(item)
21699     {
21700         this.tabs.push( item);
21701         item.navId = this.navId; // not really needed..
21702         this.addBullet();
21703     
21704     },
21705     
21706     getActivePanel : function()
21707     {
21708         var r = false;
21709         Roo.each(this.tabs, function(t) {
21710             if (t.active) {
21711                 r = t;
21712                 return false;
21713             }
21714             return null;
21715         });
21716         return r;
21717         
21718     },
21719     getPanelByName : function(n)
21720     {
21721         var r = false;
21722         Roo.each(this.tabs, function(t) {
21723             if (t.tabId == n) {
21724                 r = t;
21725                 return false;
21726             }
21727             return null;
21728         });
21729         return r;
21730     },
21731     indexOfPanel : function(p)
21732     {
21733         var r = false;
21734         Roo.each(this.tabs, function(t,i) {
21735             if (t.tabId == p.tabId) {
21736                 r = i;
21737                 return false;
21738             }
21739             return null;
21740         });
21741         return r;
21742     },
21743     /**
21744      * show a specific panel
21745      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21746      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21747      */
21748     showPanel : function (pan)
21749     {
21750         if(this.transition || typeof(pan) == 'undefined'){
21751             Roo.log("waiting for the transitionend");
21752             return false;
21753         }
21754         
21755         if (typeof(pan) == 'number') {
21756             pan = this.tabs[pan];
21757         }
21758         
21759         if (typeof(pan) == 'string') {
21760             pan = this.getPanelByName(pan);
21761         }
21762         
21763         var cur = this.getActivePanel();
21764         
21765         if(!pan || !cur){
21766             Roo.log('pan or acitve pan is undefined');
21767             return false;
21768         }
21769         
21770         if (pan.tabId == this.getActivePanel().tabId) {
21771             return true;
21772         }
21773         
21774         if (false === cur.fireEvent('beforedeactivate')) {
21775             return false;
21776         }
21777         
21778         if(this.bullets > 0 && !Roo.isTouch){
21779             this.setActiveBullet(this.indexOfPanel(pan));
21780         }
21781         
21782         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21783             
21784             //class="carousel-item carousel-item-next carousel-item-left"
21785             
21786             this.transition = true;
21787             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21788             var lr = dir == 'next' ? 'left' : 'right';
21789             pan.el.addClass(dir); // or prev
21790             pan.el.addClass('carousel-item-' + dir); // or prev
21791             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21792             cur.el.addClass(lr); // or right
21793             pan.el.addClass(lr);
21794             cur.el.addClass('carousel-item-' +lr); // or right
21795             pan.el.addClass('carousel-item-' +lr);
21796             
21797             
21798             var _this = this;
21799             cur.el.on('transitionend', function() {
21800                 Roo.log("trans end?");
21801                 
21802                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21803                 pan.setActive(true);
21804                 
21805                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21806                 cur.setActive(false);
21807                 
21808                 _this.transition = false;
21809                 
21810             }, this, { single:  true } );
21811             
21812             return true;
21813         }
21814         
21815         cur.setActive(false);
21816         pan.setActive(true);
21817         
21818         return true;
21819         
21820     },
21821     showPanelNext : function()
21822     {
21823         var i = this.indexOfPanel(this.getActivePanel());
21824         
21825         if (i >= this.tabs.length - 1 && !this.autoslide) {
21826             return;
21827         }
21828         
21829         if (i >= this.tabs.length - 1 && this.autoslide) {
21830             i = -1;
21831         }
21832         
21833         this.showPanel(this.tabs[i+1]);
21834     },
21835     
21836     showPanelPrev : function()
21837     {
21838         var i = this.indexOfPanel(this.getActivePanel());
21839         
21840         if (i  < 1 && !this.autoslide) {
21841             return;
21842         }
21843         
21844         if (i < 1 && this.autoslide) {
21845             i = this.tabs.length;
21846         }
21847         
21848         this.showPanel(this.tabs[i-1]);
21849     },
21850     
21851     
21852     addBullet: function()
21853     {
21854         if(!this.bullets || Roo.isTouch){
21855             return;
21856         }
21857         var ctr = this.el.select('.carousel-bullets',true).first();
21858         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21859         var bullet = ctr.createChild({
21860             cls : 'bullet bullet-' + i
21861         },ctr.dom.lastChild);
21862         
21863         
21864         var _this = this;
21865         
21866         bullet.on('click', (function(e, el, o, ii, t){
21867
21868             e.preventDefault();
21869
21870             this.showPanel(ii);
21871
21872             if(this.autoslide && this.slideFn){
21873                 clearInterval(this.slideFn);
21874                 this.slideFn = window.setInterval(function() {
21875                     _this.showPanelNext();
21876                 }, this.timer);
21877             }
21878
21879         }).createDelegate(this, [i, bullet], true));
21880                 
21881         
21882     },
21883      
21884     setActiveBullet : function(i)
21885     {
21886         if(Roo.isTouch){
21887             return;
21888         }
21889         
21890         Roo.each(this.el.select('.bullet', true).elements, function(el){
21891             el.removeClass('selected');
21892         });
21893
21894         var bullet = this.el.select('.bullet-' + i, true).first();
21895         
21896         if(!bullet){
21897             return;
21898         }
21899         
21900         bullet.addClass('selected');
21901     }
21902     
21903     
21904   
21905 });
21906
21907  
21908
21909  
21910  
21911 Roo.apply(Roo.bootstrap.TabGroup, {
21912     
21913     groups: {},
21914      /**
21915     * register a Navigation Group
21916     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21917     */
21918     register : function(navgrp)
21919     {
21920         this.groups[navgrp.navId] = navgrp;
21921         
21922     },
21923     /**
21924     * fetch a Navigation Group based on the navigation ID
21925     * if one does not exist , it will get created.
21926     * @param {string} the navgroup to add
21927     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21928     */
21929     get: function(navId) {
21930         if (typeof(this.groups[navId]) == 'undefined') {
21931             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21932         }
21933         return this.groups[navId] ;
21934     }
21935     
21936     
21937     
21938 });
21939
21940  /*
21941  * - LGPL
21942  *
21943  * TabPanel
21944  * 
21945  */
21946
21947 /**
21948  * @class Roo.bootstrap.TabPanel
21949  * @extends Roo.bootstrap.Component
21950  * Bootstrap TabPanel class
21951  * @cfg {Boolean} active panel active
21952  * @cfg {String} html panel content
21953  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21954  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21955  * @cfg {String} href click to link..
21956  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21957  * 
21958  * 
21959  * @constructor
21960  * Create a new TabPanel
21961  * @param {Object} config The config object
21962  */
21963
21964 Roo.bootstrap.TabPanel = function(config){
21965     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21966     this.addEvents({
21967         /**
21968              * @event changed
21969              * Fires when the active status changes
21970              * @param {Roo.bootstrap.TabPanel} this
21971              * @param {Boolean} state the new state
21972             
21973          */
21974         'changed': true,
21975         /**
21976              * @event beforedeactivate
21977              * Fires before a tab is de-activated - can be used to do validation on a form.
21978              * @param {Roo.bootstrap.TabPanel} this
21979              * @return {Boolean} false if there is an error
21980             
21981          */
21982         'beforedeactivate': true
21983      });
21984     
21985     this.tabId = this.tabId || Roo.id();
21986   
21987 };
21988
21989 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21990     
21991     active: false,
21992     html: false,
21993     tabId: false,
21994     navId : false,
21995     href : '',
21996     touchSlide : false,
21997     getAutoCreate : function(){
21998         
21999         
22000         var cfg = {
22001             tag: 'div',
22002             // item is needed for carousel - not sure if it has any effect otherwise
22003             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22004             html: this.html || ''
22005         };
22006         
22007         if(this.active){
22008             cfg.cls += ' active';
22009         }
22010         
22011         if(this.tabId){
22012             cfg.tabId = this.tabId;
22013         }
22014         
22015         
22016         
22017         return cfg;
22018     },
22019     
22020     initEvents:  function()
22021     {
22022         var p = this.parent();
22023         
22024         this.navId = this.navId || p.navId;
22025         
22026         if (typeof(this.navId) != 'undefined') {
22027             // not really needed.. but just in case.. parent should be a NavGroup.
22028             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22029             
22030             tg.register(this);
22031             
22032             var i = tg.tabs.length - 1;
22033             
22034             if(this.active && tg.bullets > 0 && i < tg.bullets){
22035                 tg.setActiveBullet(i);
22036             }
22037         }
22038         
22039         this.el.on('click', this.onClick, this);
22040         
22041         if(Roo.isTouch && this.touchSlide){
22042             this.el.on("touchstart", this.onTouchStart, this);
22043             this.el.on("touchmove", this.onTouchMove, this);
22044             this.el.on("touchend", this.onTouchEnd, this);
22045         }
22046         
22047     },
22048     
22049     onRender : function(ct, position)
22050     {
22051         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22052     },
22053     
22054     setActive : function(state)
22055     {
22056         Roo.log("panel - set active " + this.tabId + "=" + state);
22057         
22058         this.active = state;
22059         if (!state) {
22060             this.el.removeClass('active');
22061             
22062         } else  if (!this.el.hasClass('active')) {
22063             this.el.addClass('active');
22064         }
22065         
22066         this.fireEvent('changed', this, state);
22067     },
22068     
22069     onClick : function(e)
22070     {
22071         e.preventDefault();
22072         
22073         if(!this.href.length){
22074             return;
22075         }
22076         
22077         window.location.href = this.href;
22078     },
22079     
22080     startX : 0,
22081     startY : 0,
22082     endX : 0,
22083     endY : 0,
22084     swiping : false,
22085     
22086     onTouchStart : function(e)
22087     {
22088         this.swiping = false;
22089         
22090         this.startX = e.browserEvent.touches[0].clientX;
22091         this.startY = e.browserEvent.touches[0].clientY;
22092     },
22093     
22094     onTouchMove : function(e)
22095     {
22096         this.swiping = true;
22097         
22098         this.endX = e.browserEvent.touches[0].clientX;
22099         this.endY = e.browserEvent.touches[0].clientY;
22100     },
22101     
22102     onTouchEnd : function(e)
22103     {
22104         if(!this.swiping){
22105             this.onClick(e);
22106             return;
22107         }
22108         
22109         var tabGroup = this.parent();
22110         
22111         if(this.endX > this.startX){ // swiping right
22112             tabGroup.showPanelPrev();
22113             return;
22114         }
22115         
22116         if(this.startX > this.endX){ // swiping left
22117             tabGroup.showPanelNext();
22118             return;
22119         }
22120     }
22121     
22122     
22123 });
22124  
22125
22126  
22127
22128  /*
22129  * - LGPL
22130  *
22131  * DateField
22132  * 
22133  */
22134
22135 /**
22136  * @class Roo.bootstrap.DateField
22137  * @extends Roo.bootstrap.Input
22138  * Bootstrap DateField class
22139  * @cfg {Number} weekStart default 0
22140  * @cfg {String} viewMode default empty, (months|years)
22141  * @cfg {String} minViewMode default empty, (months|years)
22142  * @cfg {Number} startDate default -Infinity
22143  * @cfg {Number} endDate default Infinity
22144  * @cfg {Boolean} todayHighlight default false
22145  * @cfg {Boolean} todayBtn default false
22146  * @cfg {Boolean} calendarWeeks default false
22147  * @cfg {Object} daysOfWeekDisabled default empty
22148  * @cfg {Boolean} singleMode default false (true | false)
22149  * 
22150  * @cfg {Boolean} keyboardNavigation default true
22151  * @cfg {String} language default en
22152  * 
22153  * @constructor
22154  * Create a new DateField
22155  * @param {Object} config The config object
22156  */
22157
22158 Roo.bootstrap.DateField = function(config){
22159     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22160      this.addEvents({
22161             /**
22162              * @event show
22163              * Fires when this field show.
22164              * @param {Roo.bootstrap.DateField} this
22165              * @param {Mixed} date The date value
22166              */
22167             show : true,
22168             /**
22169              * @event show
22170              * Fires when this field hide.
22171              * @param {Roo.bootstrap.DateField} this
22172              * @param {Mixed} date The date value
22173              */
22174             hide : true,
22175             /**
22176              * @event select
22177              * Fires when select a date.
22178              * @param {Roo.bootstrap.DateField} this
22179              * @param {Mixed} date The date value
22180              */
22181             select : true,
22182             /**
22183              * @event beforeselect
22184              * Fires when before select a date.
22185              * @param {Roo.bootstrap.DateField} this
22186              * @param {Mixed} date The date value
22187              */
22188             beforeselect : true
22189         });
22190 };
22191
22192 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22193     
22194     /**
22195      * @cfg {String} format
22196      * The default date format string which can be overriden for localization support.  The format must be
22197      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22198      */
22199     format : "m/d/y",
22200     /**
22201      * @cfg {String} altFormats
22202      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22203      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22204      */
22205     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22206     
22207     weekStart : 0,
22208     
22209     viewMode : '',
22210     
22211     minViewMode : '',
22212     
22213     todayHighlight : false,
22214     
22215     todayBtn: false,
22216     
22217     language: 'en',
22218     
22219     keyboardNavigation: true,
22220     
22221     calendarWeeks: false,
22222     
22223     startDate: -Infinity,
22224     
22225     endDate: Infinity,
22226     
22227     daysOfWeekDisabled: [],
22228     
22229     _events: [],
22230     
22231     singleMode : false,
22232     
22233     UTCDate: function()
22234     {
22235         return new Date(Date.UTC.apply(Date, arguments));
22236     },
22237     
22238     UTCToday: function()
22239     {
22240         var today = new Date();
22241         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22242     },
22243     
22244     getDate: function() {
22245             var d = this.getUTCDate();
22246             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22247     },
22248     
22249     getUTCDate: function() {
22250             return this.date;
22251     },
22252     
22253     setDate: function(d) {
22254             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22255     },
22256     
22257     setUTCDate: function(d) {
22258             this.date = d;
22259             this.setValue(this.formatDate(this.date));
22260     },
22261         
22262     onRender: function(ct, position)
22263     {
22264         
22265         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22266         
22267         this.language = this.language || 'en';
22268         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22269         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22270         
22271         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22272         this.format = this.format || 'm/d/y';
22273         this.isInline = false;
22274         this.isInput = true;
22275         this.component = this.el.select('.add-on', true).first() || false;
22276         this.component = (this.component && this.component.length === 0) ? false : this.component;
22277         this.hasInput = this.component && this.inputEl().length;
22278         
22279         if (typeof(this.minViewMode === 'string')) {
22280             switch (this.minViewMode) {
22281                 case 'months':
22282                     this.minViewMode = 1;
22283                     break;
22284                 case 'years':
22285                     this.minViewMode = 2;
22286                     break;
22287                 default:
22288                     this.minViewMode = 0;
22289                     break;
22290             }
22291         }
22292         
22293         if (typeof(this.viewMode === 'string')) {
22294             switch (this.viewMode) {
22295                 case 'months':
22296                     this.viewMode = 1;
22297                     break;
22298                 case 'years':
22299                     this.viewMode = 2;
22300                     break;
22301                 default:
22302                     this.viewMode = 0;
22303                     break;
22304             }
22305         }
22306                 
22307         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22308         
22309 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22310         
22311         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22312         
22313         this.picker().on('mousedown', this.onMousedown, this);
22314         this.picker().on('click', this.onClick, this);
22315         
22316         this.picker().addClass('datepicker-dropdown');
22317         
22318         this.startViewMode = this.viewMode;
22319         
22320         if(this.singleMode){
22321             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22322                 v.setVisibilityMode(Roo.Element.DISPLAY);
22323                 v.hide();
22324             });
22325             
22326             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22327                 v.setStyle('width', '189px');
22328             });
22329         }
22330         
22331         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22332             if(!this.calendarWeeks){
22333                 v.remove();
22334                 return;
22335             }
22336             
22337             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22338             v.attr('colspan', function(i, val){
22339                 return parseInt(val) + 1;
22340             });
22341         });
22342                         
22343         
22344         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22345         
22346         this.setStartDate(this.startDate);
22347         this.setEndDate(this.endDate);
22348         
22349         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22350         
22351         this.fillDow();
22352         this.fillMonths();
22353         this.update();
22354         this.showMode();
22355         
22356         if(this.isInline) {
22357             this.showPopup();
22358         }
22359     },
22360     
22361     picker : function()
22362     {
22363         return this.pickerEl;
22364 //        return this.el.select('.datepicker', true).first();
22365     },
22366     
22367     fillDow: function()
22368     {
22369         var dowCnt = this.weekStart;
22370         
22371         var dow = {
22372             tag: 'tr',
22373             cn: [
22374                 
22375             ]
22376         };
22377         
22378         if(this.calendarWeeks){
22379             dow.cn.push({
22380                 tag: 'th',
22381                 cls: 'cw',
22382                 html: '&nbsp;'
22383             })
22384         }
22385         
22386         while (dowCnt < this.weekStart + 7) {
22387             dow.cn.push({
22388                 tag: 'th',
22389                 cls: 'dow',
22390                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22391             });
22392         }
22393         
22394         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22395     },
22396     
22397     fillMonths: function()
22398     {    
22399         var i = 0;
22400         var months = this.picker().select('>.datepicker-months td', true).first();
22401         
22402         months.dom.innerHTML = '';
22403         
22404         while (i < 12) {
22405             var month = {
22406                 tag: 'span',
22407                 cls: 'month',
22408                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22409             };
22410             
22411             months.createChild(month);
22412         }
22413         
22414     },
22415     
22416     update: function()
22417     {
22418         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;
22419         
22420         if (this.date < this.startDate) {
22421             this.viewDate = new Date(this.startDate);
22422         } else if (this.date > this.endDate) {
22423             this.viewDate = new Date(this.endDate);
22424         } else {
22425             this.viewDate = new Date(this.date);
22426         }
22427         
22428         this.fill();
22429     },
22430     
22431     fill: function() 
22432     {
22433         var d = new Date(this.viewDate),
22434                 year = d.getUTCFullYear(),
22435                 month = d.getUTCMonth(),
22436                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22437                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22438                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22439                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22440                 currentDate = this.date && this.date.valueOf(),
22441                 today = this.UTCToday();
22442         
22443         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22444         
22445 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22446         
22447 //        this.picker.select('>tfoot th.today').
22448 //                                              .text(dates[this.language].today)
22449 //                                              .toggle(this.todayBtn !== false);
22450     
22451         this.updateNavArrows();
22452         this.fillMonths();
22453                                                 
22454         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22455         
22456         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22457          
22458         prevMonth.setUTCDate(day);
22459         
22460         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22461         
22462         var nextMonth = new Date(prevMonth);
22463         
22464         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22465         
22466         nextMonth = nextMonth.valueOf();
22467         
22468         var fillMonths = false;
22469         
22470         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22471         
22472         while(prevMonth.valueOf() <= nextMonth) {
22473             var clsName = '';
22474             
22475             if (prevMonth.getUTCDay() === this.weekStart) {
22476                 if(fillMonths){
22477                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22478                 }
22479                     
22480                 fillMonths = {
22481                     tag: 'tr',
22482                     cn: []
22483                 };
22484                 
22485                 if(this.calendarWeeks){
22486                     // ISO 8601: First week contains first thursday.
22487                     // ISO also states week starts on Monday, but we can be more abstract here.
22488                     var
22489                     // Start of current week: based on weekstart/current date
22490                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22491                     // Thursday of this week
22492                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22493                     // First Thursday of year, year from thursday
22494                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22495                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22496                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22497                     
22498                     fillMonths.cn.push({
22499                         tag: 'td',
22500                         cls: 'cw',
22501                         html: calWeek
22502                     });
22503                 }
22504             }
22505             
22506             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22507                 clsName += ' old';
22508             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22509                 clsName += ' new';
22510             }
22511             if (this.todayHighlight &&
22512                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22513                 prevMonth.getUTCMonth() == today.getMonth() &&
22514                 prevMonth.getUTCDate() == today.getDate()) {
22515                 clsName += ' today';
22516             }
22517             
22518             if (currentDate && prevMonth.valueOf() === currentDate) {
22519                 clsName += ' active';
22520             }
22521             
22522             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22523                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22524                     clsName += ' disabled';
22525             }
22526             
22527             fillMonths.cn.push({
22528                 tag: 'td',
22529                 cls: 'day ' + clsName,
22530                 html: prevMonth.getDate()
22531             });
22532             
22533             prevMonth.setDate(prevMonth.getDate()+1);
22534         }
22535           
22536         var currentYear = this.date && this.date.getUTCFullYear();
22537         var currentMonth = this.date && this.date.getUTCMonth();
22538         
22539         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22540         
22541         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22542             v.removeClass('active');
22543             
22544             if(currentYear === year && k === currentMonth){
22545                 v.addClass('active');
22546             }
22547             
22548             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22549                 v.addClass('disabled');
22550             }
22551             
22552         });
22553         
22554         
22555         year = parseInt(year/10, 10) * 10;
22556         
22557         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22558         
22559         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22560         
22561         year -= 1;
22562         for (var i = -1; i < 11; i++) {
22563             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22564                 tag: 'span',
22565                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22566                 html: year
22567             });
22568             
22569             year += 1;
22570         }
22571     },
22572     
22573     showMode: function(dir) 
22574     {
22575         if (dir) {
22576             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22577         }
22578         
22579         Roo.each(this.picker().select('>div',true).elements, function(v){
22580             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22581             v.hide();
22582         });
22583         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22584     },
22585     
22586     place: function()
22587     {
22588         if(this.isInline) {
22589             return;
22590         }
22591         
22592         this.picker().removeClass(['bottom', 'top']);
22593         
22594         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22595             /*
22596              * place to the top of element!
22597              *
22598              */
22599             
22600             this.picker().addClass('top');
22601             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22602             
22603             return;
22604         }
22605         
22606         this.picker().addClass('bottom');
22607         
22608         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22609     },
22610     
22611     parseDate : function(value)
22612     {
22613         if(!value || value instanceof Date){
22614             return value;
22615         }
22616         var v = Date.parseDate(value, this.format);
22617         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22618             v = Date.parseDate(value, 'Y-m-d');
22619         }
22620         if(!v && this.altFormats){
22621             if(!this.altFormatsArray){
22622                 this.altFormatsArray = this.altFormats.split("|");
22623             }
22624             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22625                 v = Date.parseDate(value, this.altFormatsArray[i]);
22626             }
22627         }
22628         return v;
22629     },
22630     
22631     formatDate : function(date, fmt)
22632     {   
22633         return (!date || !(date instanceof Date)) ?
22634         date : date.dateFormat(fmt || this.format);
22635     },
22636     
22637     onFocus : function()
22638     {
22639         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22640         this.showPopup();
22641     },
22642     
22643     onBlur : function()
22644     {
22645         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22646         
22647         var d = this.inputEl().getValue();
22648         
22649         this.setValue(d);
22650                 
22651         this.hidePopup();
22652     },
22653     
22654     showPopup : function()
22655     {
22656         this.picker().show();
22657         this.update();
22658         this.place();
22659         
22660         this.fireEvent('showpopup', this, this.date);
22661     },
22662     
22663     hidePopup : function()
22664     {
22665         if(this.isInline) {
22666             return;
22667         }
22668         this.picker().hide();
22669         this.viewMode = this.startViewMode;
22670         this.showMode();
22671         
22672         this.fireEvent('hidepopup', this, this.date);
22673         
22674     },
22675     
22676     onMousedown: function(e)
22677     {
22678         e.stopPropagation();
22679         e.preventDefault();
22680     },
22681     
22682     keyup: function(e)
22683     {
22684         Roo.bootstrap.DateField.superclass.keyup.call(this);
22685         this.update();
22686     },
22687
22688     setValue: function(v)
22689     {
22690         if(this.fireEvent('beforeselect', this, v) !== false){
22691             var d = new Date(this.parseDate(v) ).clearTime();
22692         
22693             if(isNaN(d.getTime())){
22694                 this.date = this.viewDate = '';
22695                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22696                 return;
22697             }
22698
22699             v = this.formatDate(d);
22700
22701             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22702
22703             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22704
22705             this.update();
22706
22707             this.fireEvent('select', this, this.date);
22708         }
22709     },
22710     
22711     getValue: function()
22712     {
22713         return this.formatDate(this.date);
22714     },
22715     
22716     fireKey: function(e)
22717     {
22718         if (!this.picker().isVisible()){
22719             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22720                 this.showPopup();
22721             }
22722             return;
22723         }
22724         
22725         var dateChanged = false,
22726         dir, day, month,
22727         newDate, newViewDate;
22728         
22729         switch(e.keyCode){
22730             case 27: // escape
22731                 this.hidePopup();
22732                 e.preventDefault();
22733                 break;
22734             case 37: // left
22735             case 39: // right
22736                 if (!this.keyboardNavigation) {
22737                     break;
22738                 }
22739                 dir = e.keyCode == 37 ? -1 : 1;
22740                 
22741                 if (e.ctrlKey){
22742                     newDate = this.moveYear(this.date, dir);
22743                     newViewDate = this.moveYear(this.viewDate, dir);
22744                 } else if (e.shiftKey){
22745                     newDate = this.moveMonth(this.date, dir);
22746                     newViewDate = this.moveMonth(this.viewDate, dir);
22747                 } else {
22748                     newDate = new Date(this.date);
22749                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22750                     newViewDate = new Date(this.viewDate);
22751                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22752                 }
22753                 if (this.dateWithinRange(newDate)){
22754                     this.date = newDate;
22755                     this.viewDate = newViewDate;
22756                     this.setValue(this.formatDate(this.date));
22757 //                    this.update();
22758                     e.preventDefault();
22759                     dateChanged = true;
22760                 }
22761                 break;
22762             case 38: // up
22763             case 40: // down
22764                 if (!this.keyboardNavigation) {
22765                     break;
22766                 }
22767                 dir = e.keyCode == 38 ? -1 : 1;
22768                 if (e.ctrlKey){
22769                     newDate = this.moveYear(this.date, dir);
22770                     newViewDate = this.moveYear(this.viewDate, dir);
22771                 } else if (e.shiftKey){
22772                     newDate = this.moveMonth(this.date, dir);
22773                     newViewDate = this.moveMonth(this.viewDate, dir);
22774                 } else {
22775                     newDate = new Date(this.date);
22776                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22777                     newViewDate = new Date(this.viewDate);
22778                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22779                 }
22780                 if (this.dateWithinRange(newDate)){
22781                     this.date = newDate;
22782                     this.viewDate = newViewDate;
22783                     this.setValue(this.formatDate(this.date));
22784 //                    this.update();
22785                     e.preventDefault();
22786                     dateChanged = true;
22787                 }
22788                 break;
22789             case 13: // enter
22790                 this.setValue(this.formatDate(this.date));
22791                 this.hidePopup();
22792                 e.preventDefault();
22793                 break;
22794             case 9: // tab
22795                 this.setValue(this.formatDate(this.date));
22796                 this.hidePopup();
22797                 break;
22798             case 16: // shift
22799             case 17: // ctrl
22800             case 18: // alt
22801                 break;
22802             default :
22803                 this.hidePopup();
22804                 
22805         }
22806     },
22807     
22808     
22809     onClick: function(e) 
22810     {
22811         e.stopPropagation();
22812         e.preventDefault();
22813         
22814         var target = e.getTarget();
22815         
22816         if(target.nodeName.toLowerCase() === 'i'){
22817             target = Roo.get(target).dom.parentNode;
22818         }
22819         
22820         var nodeName = target.nodeName;
22821         var className = target.className;
22822         var html = target.innerHTML;
22823         //Roo.log(nodeName);
22824         
22825         switch(nodeName.toLowerCase()) {
22826             case 'th':
22827                 switch(className) {
22828                     case 'switch':
22829                         this.showMode(1);
22830                         break;
22831                     case 'prev':
22832                     case 'next':
22833                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22834                         switch(this.viewMode){
22835                                 case 0:
22836                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22837                                         break;
22838                                 case 1:
22839                                 case 2:
22840                                         this.viewDate = this.moveYear(this.viewDate, dir);
22841                                         break;
22842                         }
22843                         this.fill();
22844                         break;
22845                     case 'today':
22846                         var date = new Date();
22847                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22848 //                        this.fill()
22849                         this.setValue(this.formatDate(this.date));
22850                         
22851                         this.hidePopup();
22852                         break;
22853                 }
22854                 break;
22855             case 'span':
22856                 if (className.indexOf('disabled') < 0) {
22857                 if (!this.viewDate) {
22858                     this.viewDate = new Date();
22859                 }
22860                 this.viewDate.setUTCDate(1);
22861                     if (className.indexOf('month') > -1) {
22862                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22863                     } else {
22864                         var year = parseInt(html, 10) || 0;
22865                         this.viewDate.setUTCFullYear(year);
22866                         
22867                     }
22868                     
22869                     if(this.singleMode){
22870                         this.setValue(this.formatDate(this.viewDate));
22871                         this.hidePopup();
22872                         return;
22873                     }
22874                     
22875                     this.showMode(-1);
22876                     this.fill();
22877                 }
22878                 break;
22879                 
22880             case 'td':
22881                 //Roo.log(className);
22882                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22883                     var day = parseInt(html, 10) || 1;
22884                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22885                         month = (this.viewDate || new Date()).getUTCMonth();
22886
22887                     if (className.indexOf('old') > -1) {
22888                         if(month === 0 ){
22889                             month = 11;
22890                             year -= 1;
22891                         }else{
22892                             month -= 1;
22893                         }
22894                     } else if (className.indexOf('new') > -1) {
22895                         if (month == 11) {
22896                             month = 0;
22897                             year += 1;
22898                         } else {
22899                             month += 1;
22900                         }
22901                     }
22902                     //Roo.log([year,month,day]);
22903                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22904                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22905 //                    this.fill();
22906                     //Roo.log(this.formatDate(this.date));
22907                     this.setValue(this.formatDate(this.date));
22908                     this.hidePopup();
22909                 }
22910                 break;
22911         }
22912     },
22913     
22914     setStartDate: function(startDate)
22915     {
22916         this.startDate = startDate || -Infinity;
22917         if (this.startDate !== -Infinity) {
22918             this.startDate = this.parseDate(this.startDate);
22919         }
22920         this.update();
22921         this.updateNavArrows();
22922     },
22923
22924     setEndDate: function(endDate)
22925     {
22926         this.endDate = endDate || Infinity;
22927         if (this.endDate !== Infinity) {
22928             this.endDate = this.parseDate(this.endDate);
22929         }
22930         this.update();
22931         this.updateNavArrows();
22932     },
22933     
22934     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22935     {
22936         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22937         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22938             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22939         }
22940         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22941             return parseInt(d, 10);
22942         });
22943         this.update();
22944         this.updateNavArrows();
22945     },
22946     
22947     updateNavArrows: function() 
22948     {
22949         if(this.singleMode){
22950             return;
22951         }
22952         
22953         var d = new Date(this.viewDate),
22954         year = d.getUTCFullYear(),
22955         month = d.getUTCMonth();
22956         
22957         Roo.each(this.picker().select('.prev', true).elements, function(v){
22958             v.show();
22959             switch (this.viewMode) {
22960                 case 0:
22961
22962                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22963                         v.hide();
22964                     }
22965                     break;
22966                 case 1:
22967                 case 2:
22968                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22969                         v.hide();
22970                     }
22971                     break;
22972             }
22973         });
22974         
22975         Roo.each(this.picker().select('.next', true).elements, function(v){
22976             v.show();
22977             switch (this.viewMode) {
22978                 case 0:
22979
22980                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22981                         v.hide();
22982                     }
22983                     break;
22984                 case 1:
22985                 case 2:
22986                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22987                         v.hide();
22988                     }
22989                     break;
22990             }
22991         })
22992     },
22993     
22994     moveMonth: function(date, dir)
22995     {
22996         if (!dir) {
22997             return date;
22998         }
22999         var new_date = new Date(date.valueOf()),
23000         day = new_date.getUTCDate(),
23001         month = new_date.getUTCMonth(),
23002         mag = Math.abs(dir),
23003         new_month, test;
23004         dir = dir > 0 ? 1 : -1;
23005         if (mag == 1){
23006             test = dir == -1
23007             // If going back one month, make sure month is not current month
23008             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23009             ? function(){
23010                 return new_date.getUTCMonth() == month;
23011             }
23012             // If going forward one month, make sure month is as expected
23013             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23014             : function(){
23015                 return new_date.getUTCMonth() != new_month;
23016             };
23017             new_month = month + dir;
23018             new_date.setUTCMonth(new_month);
23019             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23020             if (new_month < 0 || new_month > 11) {
23021                 new_month = (new_month + 12) % 12;
23022             }
23023         } else {
23024             // For magnitudes >1, move one month at a time...
23025             for (var i=0; i<mag; i++) {
23026                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23027                 new_date = this.moveMonth(new_date, dir);
23028             }
23029             // ...then reset the day, keeping it in the new month
23030             new_month = new_date.getUTCMonth();
23031             new_date.setUTCDate(day);
23032             test = function(){
23033                 return new_month != new_date.getUTCMonth();
23034             };
23035         }
23036         // Common date-resetting loop -- if date is beyond end of month, make it
23037         // end of month
23038         while (test()){
23039             new_date.setUTCDate(--day);
23040             new_date.setUTCMonth(new_month);
23041         }
23042         return new_date;
23043     },
23044
23045     moveYear: function(date, dir)
23046     {
23047         return this.moveMonth(date, dir*12);
23048     },
23049
23050     dateWithinRange: function(date)
23051     {
23052         return date >= this.startDate && date <= this.endDate;
23053     },
23054
23055     
23056     remove: function() 
23057     {
23058         this.picker().remove();
23059     },
23060     
23061     validateValue : function(value)
23062     {
23063         if(this.getVisibilityEl().hasClass('hidden')){
23064             return true;
23065         }
23066         
23067         if(value.length < 1)  {
23068             if(this.allowBlank){
23069                 return true;
23070             }
23071             return false;
23072         }
23073         
23074         if(value.length < this.minLength){
23075             return false;
23076         }
23077         if(value.length > this.maxLength){
23078             return false;
23079         }
23080         if(this.vtype){
23081             var vt = Roo.form.VTypes;
23082             if(!vt[this.vtype](value, this)){
23083                 return false;
23084             }
23085         }
23086         if(typeof this.validator == "function"){
23087             var msg = this.validator(value);
23088             if(msg !== true){
23089                 return false;
23090             }
23091         }
23092         
23093         if(this.regex && !this.regex.test(value)){
23094             return false;
23095         }
23096         
23097         if(typeof(this.parseDate(value)) == 'undefined'){
23098             return false;
23099         }
23100         
23101         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23102             return false;
23103         }      
23104         
23105         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23106             return false;
23107         } 
23108         
23109         
23110         return true;
23111     },
23112     
23113     reset : function()
23114     {
23115         this.date = this.viewDate = '';
23116         
23117         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23118     }
23119    
23120 });
23121
23122 Roo.apply(Roo.bootstrap.DateField,  {
23123     
23124     head : {
23125         tag: 'thead',
23126         cn: [
23127         {
23128             tag: 'tr',
23129             cn: [
23130             {
23131                 tag: 'th',
23132                 cls: 'prev',
23133                 html: '<i class="fa fa-arrow-left"/>'
23134             },
23135             {
23136                 tag: 'th',
23137                 cls: 'switch',
23138                 colspan: '5'
23139             },
23140             {
23141                 tag: 'th',
23142                 cls: 'next',
23143                 html: '<i class="fa fa-arrow-right"/>'
23144             }
23145
23146             ]
23147         }
23148         ]
23149     },
23150     
23151     content : {
23152         tag: 'tbody',
23153         cn: [
23154         {
23155             tag: 'tr',
23156             cn: [
23157             {
23158                 tag: 'td',
23159                 colspan: '7'
23160             }
23161             ]
23162         }
23163         ]
23164     },
23165     
23166     footer : {
23167         tag: 'tfoot',
23168         cn: [
23169         {
23170             tag: 'tr',
23171             cn: [
23172             {
23173                 tag: 'th',
23174                 colspan: '7',
23175                 cls: 'today'
23176             }
23177                     
23178             ]
23179         }
23180         ]
23181     },
23182     
23183     dates:{
23184         en: {
23185             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23186             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23187             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23188             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23189             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23190             today: "Today"
23191         }
23192     },
23193     
23194     modes: [
23195     {
23196         clsName: 'days',
23197         navFnc: 'Month',
23198         navStep: 1
23199     },
23200     {
23201         clsName: 'months',
23202         navFnc: 'FullYear',
23203         navStep: 1
23204     },
23205     {
23206         clsName: 'years',
23207         navFnc: 'FullYear',
23208         navStep: 10
23209     }]
23210 });
23211
23212 Roo.apply(Roo.bootstrap.DateField,  {
23213   
23214     template : {
23215         tag: 'div',
23216         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23217         cn: [
23218         {
23219             tag: 'div',
23220             cls: 'datepicker-days',
23221             cn: [
23222             {
23223                 tag: 'table',
23224                 cls: 'table-condensed',
23225                 cn:[
23226                 Roo.bootstrap.DateField.head,
23227                 {
23228                     tag: 'tbody'
23229                 },
23230                 Roo.bootstrap.DateField.footer
23231                 ]
23232             }
23233             ]
23234         },
23235         {
23236             tag: 'div',
23237             cls: 'datepicker-months',
23238             cn: [
23239             {
23240                 tag: 'table',
23241                 cls: 'table-condensed',
23242                 cn:[
23243                 Roo.bootstrap.DateField.head,
23244                 Roo.bootstrap.DateField.content,
23245                 Roo.bootstrap.DateField.footer
23246                 ]
23247             }
23248             ]
23249         },
23250         {
23251             tag: 'div',
23252             cls: 'datepicker-years',
23253             cn: [
23254             {
23255                 tag: 'table',
23256                 cls: 'table-condensed',
23257                 cn:[
23258                 Roo.bootstrap.DateField.head,
23259                 Roo.bootstrap.DateField.content,
23260                 Roo.bootstrap.DateField.footer
23261                 ]
23262             }
23263             ]
23264         }
23265         ]
23266     }
23267 });
23268
23269  
23270
23271  /*
23272  * - LGPL
23273  *
23274  * TimeField
23275  * 
23276  */
23277
23278 /**
23279  * @class Roo.bootstrap.TimeField
23280  * @extends Roo.bootstrap.Input
23281  * Bootstrap DateField class
23282  * 
23283  * 
23284  * @constructor
23285  * Create a new TimeField
23286  * @param {Object} config The config object
23287  */
23288
23289 Roo.bootstrap.TimeField = function(config){
23290     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23291     this.addEvents({
23292             /**
23293              * @event show
23294              * Fires when this field show.
23295              * @param {Roo.bootstrap.DateField} thisthis
23296              * @param {Mixed} date The date value
23297              */
23298             show : true,
23299             /**
23300              * @event show
23301              * Fires when this field hide.
23302              * @param {Roo.bootstrap.DateField} this
23303              * @param {Mixed} date The date value
23304              */
23305             hide : true,
23306             /**
23307              * @event select
23308              * Fires when select a date.
23309              * @param {Roo.bootstrap.DateField} this
23310              * @param {Mixed} date The date value
23311              */
23312             select : true
23313         });
23314 };
23315
23316 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23317     
23318     /**
23319      * @cfg {String} format
23320      * The default time format string which can be overriden for localization support.  The format must be
23321      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23322      */
23323     format : "H:i",
23324
23325     getAutoCreate : function()
23326     {
23327         this.after = '<i class="fa far fa-clock"></i>';
23328         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23329         
23330          
23331     },
23332     onRender: function(ct, position)
23333     {
23334         
23335         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23336                 
23337         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23338         
23339         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23340         
23341         this.pop = this.picker().select('>.datepicker-time',true).first();
23342         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23343         
23344         this.picker().on('mousedown', this.onMousedown, this);
23345         this.picker().on('click', this.onClick, this);
23346         
23347         this.picker().addClass('datepicker-dropdown');
23348     
23349         this.fillTime();
23350         this.update();
23351             
23352         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23353         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23354         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23355         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23356         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23357         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23358
23359     },
23360     
23361     fireKey: function(e){
23362         if (!this.picker().isVisible()){
23363             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23364                 this.show();
23365             }
23366             return;
23367         }
23368
23369         e.preventDefault();
23370         
23371         switch(e.keyCode){
23372             case 27: // escape
23373                 this.hide();
23374                 break;
23375             case 37: // left
23376             case 39: // right
23377                 this.onTogglePeriod();
23378                 break;
23379             case 38: // up
23380                 this.onIncrementMinutes();
23381                 break;
23382             case 40: // down
23383                 this.onDecrementMinutes();
23384                 break;
23385             case 13: // enter
23386             case 9: // tab
23387                 this.setTime();
23388                 break;
23389         }
23390     },
23391     
23392     onClick: function(e) {
23393         e.stopPropagation();
23394         e.preventDefault();
23395     },
23396     
23397     picker : function()
23398     {
23399         return this.pickerEl;
23400     },
23401     
23402     fillTime: function()
23403     {    
23404         var time = this.pop.select('tbody', true).first();
23405         
23406         time.dom.innerHTML = '';
23407         
23408         time.createChild({
23409             tag: 'tr',
23410             cn: [
23411                 {
23412                     tag: 'td',
23413                     cn: [
23414                         {
23415                             tag: 'a',
23416                             href: '#',
23417                             cls: 'btn',
23418                             cn: [
23419                                 {
23420                                     tag: 'i',
23421                                     cls: 'hours-up fa fas fa-chevron-up'
23422                                 }
23423                             ]
23424                         } 
23425                     ]
23426                 },
23427                 {
23428                     tag: 'td',
23429                     cls: 'separator'
23430                 },
23431                 {
23432                     tag: 'td',
23433                     cn: [
23434                         {
23435                             tag: 'a',
23436                             href: '#',
23437                             cls: 'btn',
23438                             cn: [
23439                                 {
23440                                     tag: 'i',
23441                                     cls: 'minutes-up fa fas fa-chevron-up'
23442                                 }
23443                             ]
23444                         }
23445                     ]
23446                 },
23447                 {
23448                     tag: 'td',
23449                     cls: 'separator'
23450                 }
23451             ]
23452         });
23453         
23454         time.createChild({
23455             tag: 'tr',
23456             cn: [
23457                 {
23458                     tag: 'td',
23459                     cn: [
23460                         {
23461                             tag: 'span',
23462                             cls: 'timepicker-hour',
23463                             html: '00'
23464                         }  
23465                     ]
23466                 },
23467                 {
23468                     tag: 'td',
23469                     cls: 'separator',
23470                     html: ':'
23471                 },
23472                 {
23473                     tag: 'td',
23474                     cn: [
23475                         {
23476                             tag: 'span',
23477                             cls: 'timepicker-minute',
23478                             html: '00'
23479                         }  
23480                     ]
23481                 },
23482                 {
23483                     tag: 'td',
23484                     cls: 'separator'
23485                 },
23486                 {
23487                     tag: 'td',
23488                     cn: [
23489                         {
23490                             tag: 'button',
23491                             type: 'button',
23492                             cls: 'btn btn-primary period',
23493                             html: 'AM'
23494                             
23495                         }
23496                     ]
23497                 }
23498             ]
23499         });
23500         
23501         time.createChild({
23502             tag: 'tr',
23503             cn: [
23504                 {
23505                     tag: 'td',
23506                     cn: [
23507                         {
23508                             tag: 'a',
23509                             href: '#',
23510                             cls: 'btn',
23511                             cn: [
23512                                 {
23513                                     tag: 'span',
23514                                     cls: 'hours-down fa fas fa-chevron-down'
23515                                 }
23516                             ]
23517                         }
23518                     ]
23519                 },
23520                 {
23521                     tag: 'td',
23522                     cls: 'separator'
23523                 },
23524                 {
23525                     tag: 'td',
23526                     cn: [
23527                         {
23528                             tag: 'a',
23529                             href: '#',
23530                             cls: 'btn',
23531                             cn: [
23532                                 {
23533                                     tag: 'span',
23534                                     cls: 'minutes-down fa fas fa-chevron-down'
23535                                 }
23536                             ]
23537                         }
23538                     ]
23539                 },
23540                 {
23541                     tag: 'td',
23542                     cls: 'separator'
23543                 }
23544             ]
23545         });
23546         
23547     },
23548     
23549     update: function()
23550     {
23551         
23552         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23553         
23554         this.fill();
23555     },
23556     
23557     fill: function() 
23558     {
23559         var hours = this.time.getHours();
23560         var minutes = this.time.getMinutes();
23561         var period = 'AM';
23562         
23563         if(hours > 11){
23564             period = 'PM';
23565         }
23566         
23567         if(hours == 0){
23568             hours = 12;
23569         }
23570         
23571         
23572         if(hours > 12){
23573             hours = hours - 12;
23574         }
23575         
23576         if(hours < 10){
23577             hours = '0' + hours;
23578         }
23579         
23580         if(minutes < 10){
23581             minutes = '0' + minutes;
23582         }
23583         
23584         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23585         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23586         this.pop.select('button', true).first().dom.innerHTML = period;
23587         
23588     },
23589     
23590     place: function()
23591     {   
23592         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23593         
23594         var cls = ['bottom'];
23595         
23596         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23597             cls.pop();
23598             cls.push('top');
23599         }
23600         
23601         cls.push('right');
23602         
23603         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23604             cls.pop();
23605             cls.push('left');
23606         }
23607         //this.picker().setXY(20000,20000);
23608         this.picker().addClass(cls.join('-'));
23609         
23610         var _this = this;
23611         
23612         Roo.each(cls, function(c){
23613             if(c == 'bottom'){
23614                 (function() {
23615                  //  
23616                 }).defer(200);
23617                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23618                 //_this.picker().setTop(_this.inputEl().getHeight());
23619                 return;
23620             }
23621             if(c == 'top'){
23622                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23623                 
23624                 //_this.picker().setTop(0 - _this.picker().getHeight());
23625                 return;
23626             }
23627             /*
23628             if(c == 'left'){
23629                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23630                 return;
23631             }
23632             if(c == 'right'){
23633                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23634                 return;
23635             }
23636             */
23637         });
23638         
23639     },
23640   
23641     onFocus : function()
23642     {
23643         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23644         this.show();
23645     },
23646     
23647     onBlur : function()
23648     {
23649         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23650         this.hide();
23651     },
23652     
23653     show : function()
23654     {
23655         this.picker().show();
23656         this.pop.show();
23657         this.update();
23658         this.place();
23659         
23660         this.fireEvent('show', this, this.date);
23661     },
23662     
23663     hide : function()
23664     {
23665         this.picker().hide();
23666         this.pop.hide();
23667         
23668         this.fireEvent('hide', this, this.date);
23669     },
23670     
23671     setTime : function()
23672     {
23673         this.hide();
23674         this.setValue(this.time.format(this.format));
23675         
23676         this.fireEvent('select', this, this.date);
23677         
23678         
23679     },
23680     
23681     onMousedown: function(e){
23682         e.stopPropagation();
23683         e.preventDefault();
23684     },
23685     
23686     onIncrementHours: function()
23687     {
23688         Roo.log('onIncrementHours');
23689         this.time = this.time.add(Date.HOUR, 1);
23690         this.update();
23691         
23692     },
23693     
23694     onDecrementHours: function()
23695     {
23696         Roo.log('onDecrementHours');
23697         this.time = this.time.add(Date.HOUR, -1);
23698         this.update();
23699     },
23700     
23701     onIncrementMinutes: function()
23702     {
23703         Roo.log('onIncrementMinutes');
23704         this.time = this.time.add(Date.MINUTE, 1);
23705         this.update();
23706     },
23707     
23708     onDecrementMinutes: function()
23709     {
23710         Roo.log('onDecrementMinutes');
23711         this.time = this.time.add(Date.MINUTE, -1);
23712         this.update();
23713     },
23714     
23715     onTogglePeriod: function()
23716     {
23717         Roo.log('onTogglePeriod');
23718         this.time = this.time.add(Date.HOUR, 12);
23719         this.update();
23720     }
23721     
23722    
23723 });
23724  
23725
23726 Roo.apply(Roo.bootstrap.TimeField,  {
23727   
23728     template : {
23729         tag: 'div',
23730         cls: 'datepicker dropdown-menu',
23731         cn: [
23732             {
23733                 tag: 'div',
23734                 cls: 'datepicker-time',
23735                 cn: [
23736                 {
23737                     tag: 'table',
23738                     cls: 'table-condensed',
23739                     cn:[
23740                         {
23741                             tag: 'tbody',
23742                             cn: [
23743                                 {
23744                                     tag: 'tr',
23745                                     cn: [
23746                                     {
23747                                         tag: 'td',
23748                                         colspan: '7'
23749                                     }
23750                                     ]
23751                                 }
23752                             ]
23753                         },
23754                         {
23755                             tag: 'tfoot',
23756                             cn: [
23757                                 {
23758                                     tag: 'tr',
23759                                     cn: [
23760                                     {
23761                                         tag: 'th',
23762                                         colspan: '7',
23763                                         cls: '',
23764                                         cn: [
23765                                             {
23766                                                 tag: 'button',
23767                                                 cls: 'btn btn-info ok',
23768                                                 html: 'OK'
23769                                             }
23770                                         ]
23771                                     }
23772                     
23773                                     ]
23774                                 }
23775                             ]
23776                         }
23777                     ]
23778                 }
23779                 ]
23780             }
23781         ]
23782     }
23783 });
23784
23785  
23786
23787  /*
23788  * - LGPL
23789  *
23790  * MonthField
23791  * 
23792  */
23793
23794 /**
23795  * @class Roo.bootstrap.MonthField
23796  * @extends Roo.bootstrap.Input
23797  * Bootstrap MonthField class
23798  * 
23799  * @cfg {String} language default en
23800  * 
23801  * @constructor
23802  * Create a new MonthField
23803  * @param {Object} config The config object
23804  */
23805
23806 Roo.bootstrap.MonthField = function(config){
23807     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23808     
23809     this.addEvents({
23810         /**
23811          * @event show
23812          * Fires when this field show.
23813          * @param {Roo.bootstrap.MonthField} this
23814          * @param {Mixed} date The date value
23815          */
23816         show : true,
23817         /**
23818          * @event show
23819          * Fires when this field hide.
23820          * @param {Roo.bootstrap.MonthField} this
23821          * @param {Mixed} date The date value
23822          */
23823         hide : true,
23824         /**
23825          * @event select
23826          * Fires when select a date.
23827          * @param {Roo.bootstrap.MonthField} this
23828          * @param {String} oldvalue The old value
23829          * @param {String} newvalue The new value
23830          */
23831         select : true
23832     });
23833 };
23834
23835 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23836     
23837     onRender: function(ct, position)
23838     {
23839         
23840         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23841         
23842         this.language = this.language || 'en';
23843         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23844         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23845         
23846         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23847         this.isInline = false;
23848         this.isInput = true;
23849         this.component = this.el.select('.add-on', true).first() || false;
23850         this.component = (this.component && this.component.length === 0) ? false : this.component;
23851         this.hasInput = this.component && this.inputEL().length;
23852         
23853         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23854         
23855         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23856         
23857         this.picker().on('mousedown', this.onMousedown, this);
23858         this.picker().on('click', this.onClick, this);
23859         
23860         this.picker().addClass('datepicker-dropdown');
23861         
23862         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23863             v.setStyle('width', '189px');
23864         });
23865         
23866         this.fillMonths();
23867         
23868         this.update();
23869         
23870         if(this.isInline) {
23871             this.show();
23872         }
23873         
23874     },
23875     
23876     setValue: function(v, suppressEvent)
23877     {   
23878         var o = this.getValue();
23879         
23880         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23881         
23882         this.update();
23883
23884         if(suppressEvent !== true){
23885             this.fireEvent('select', this, o, v);
23886         }
23887         
23888     },
23889     
23890     getValue: function()
23891     {
23892         return this.value;
23893     },
23894     
23895     onClick: function(e) 
23896     {
23897         e.stopPropagation();
23898         e.preventDefault();
23899         
23900         var target = e.getTarget();
23901         
23902         if(target.nodeName.toLowerCase() === 'i'){
23903             target = Roo.get(target).dom.parentNode;
23904         }
23905         
23906         var nodeName = target.nodeName;
23907         var className = target.className;
23908         var html = target.innerHTML;
23909         
23910         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23911             return;
23912         }
23913         
23914         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23915         
23916         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23917         
23918         this.hide();
23919                         
23920     },
23921     
23922     picker : function()
23923     {
23924         return this.pickerEl;
23925     },
23926     
23927     fillMonths: function()
23928     {    
23929         var i = 0;
23930         var months = this.picker().select('>.datepicker-months td', true).first();
23931         
23932         months.dom.innerHTML = '';
23933         
23934         while (i < 12) {
23935             var month = {
23936                 tag: 'span',
23937                 cls: 'month',
23938                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23939             };
23940             
23941             months.createChild(month);
23942         }
23943         
23944     },
23945     
23946     update: function()
23947     {
23948         var _this = this;
23949         
23950         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23951             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23952         }
23953         
23954         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23955             e.removeClass('active');
23956             
23957             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23958                 e.addClass('active');
23959             }
23960         })
23961     },
23962     
23963     place: function()
23964     {
23965         if(this.isInline) {
23966             return;
23967         }
23968         
23969         this.picker().removeClass(['bottom', 'top']);
23970         
23971         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23972             /*
23973              * place to the top of element!
23974              *
23975              */
23976             
23977             this.picker().addClass('top');
23978             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23979             
23980             return;
23981         }
23982         
23983         this.picker().addClass('bottom');
23984         
23985         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23986     },
23987     
23988     onFocus : function()
23989     {
23990         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23991         this.show();
23992     },
23993     
23994     onBlur : function()
23995     {
23996         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23997         
23998         var d = this.inputEl().getValue();
23999         
24000         this.setValue(d);
24001                 
24002         this.hide();
24003     },
24004     
24005     show : function()
24006     {
24007         this.picker().show();
24008         this.picker().select('>.datepicker-months', true).first().show();
24009         this.update();
24010         this.place();
24011         
24012         this.fireEvent('show', this, this.date);
24013     },
24014     
24015     hide : function()
24016     {
24017         if(this.isInline) {
24018             return;
24019         }
24020         this.picker().hide();
24021         this.fireEvent('hide', this, this.date);
24022         
24023     },
24024     
24025     onMousedown: function(e)
24026     {
24027         e.stopPropagation();
24028         e.preventDefault();
24029     },
24030     
24031     keyup: function(e)
24032     {
24033         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24034         this.update();
24035     },
24036
24037     fireKey: function(e)
24038     {
24039         if (!this.picker().isVisible()){
24040             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24041                 this.show();
24042             }
24043             return;
24044         }
24045         
24046         var dir;
24047         
24048         switch(e.keyCode){
24049             case 27: // escape
24050                 this.hide();
24051                 e.preventDefault();
24052                 break;
24053             case 37: // left
24054             case 39: // right
24055                 dir = e.keyCode == 37 ? -1 : 1;
24056                 
24057                 this.vIndex = this.vIndex + dir;
24058                 
24059                 if(this.vIndex < 0){
24060                     this.vIndex = 0;
24061                 }
24062                 
24063                 if(this.vIndex > 11){
24064                     this.vIndex = 11;
24065                 }
24066                 
24067                 if(isNaN(this.vIndex)){
24068                     this.vIndex = 0;
24069                 }
24070                 
24071                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24072                 
24073                 break;
24074             case 38: // up
24075             case 40: // down
24076                 
24077                 dir = e.keyCode == 38 ? -1 : 1;
24078                 
24079                 this.vIndex = this.vIndex + dir * 4;
24080                 
24081                 if(this.vIndex < 0){
24082                     this.vIndex = 0;
24083                 }
24084                 
24085                 if(this.vIndex > 11){
24086                     this.vIndex = 11;
24087                 }
24088                 
24089                 if(isNaN(this.vIndex)){
24090                     this.vIndex = 0;
24091                 }
24092                 
24093                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24094                 break;
24095                 
24096             case 13: // enter
24097                 
24098                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24099                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24100                 }
24101                 
24102                 this.hide();
24103                 e.preventDefault();
24104                 break;
24105             case 9: // tab
24106                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24107                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24108                 }
24109                 this.hide();
24110                 break;
24111             case 16: // shift
24112             case 17: // ctrl
24113             case 18: // alt
24114                 break;
24115             default :
24116                 this.hide();
24117                 
24118         }
24119     },
24120     
24121     remove: function() 
24122     {
24123         this.picker().remove();
24124     }
24125    
24126 });
24127
24128 Roo.apply(Roo.bootstrap.MonthField,  {
24129     
24130     content : {
24131         tag: 'tbody',
24132         cn: [
24133         {
24134             tag: 'tr',
24135             cn: [
24136             {
24137                 tag: 'td',
24138                 colspan: '7'
24139             }
24140             ]
24141         }
24142         ]
24143     },
24144     
24145     dates:{
24146         en: {
24147             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24148             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24149         }
24150     }
24151 });
24152
24153 Roo.apply(Roo.bootstrap.MonthField,  {
24154   
24155     template : {
24156         tag: 'div',
24157         cls: 'datepicker dropdown-menu roo-dynamic',
24158         cn: [
24159             {
24160                 tag: 'div',
24161                 cls: 'datepicker-months',
24162                 cn: [
24163                 {
24164                     tag: 'table',
24165                     cls: 'table-condensed',
24166                     cn:[
24167                         Roo.bootstrap.DateField.content
24168                     ]
24169                 }
24170                 ]
24171             }
24172         ]
24173     }
24174 });
24175
24176  
24177
24178  
24179  /*
24180  * - LGPL
24181  *
24182  * CheckBox
24183  * 
24184  */
24185
24186 /**
24187  * @class Roo.bootstrap.CheckBox
24188  * @extends Roo.bootstrap.Input
24189  * Bootstrap CheckBox class
24190  * 
24191  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24192  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24193  * @cfg {String} boxLabel The text that appears beside the checkbox
24194  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24195  * @cfg {Boolean} checked initnal the element
24196  * @cfg {Boolean} inline inline the element (default false)
24197  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24198  * @cfg {String} tooltip label tooltip
24199  * 
24200  * @constructor
24201  * Create a new CheckBox
24202  * @param {Object} config The config object
24203  */
24204
24205 Roo.bootstrap.CheckBox = function(config){
24206     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24207    
24208     this.addEvents({
24209         /**
24210         * @event check
24211         * Fires when the element is checked or unchecked.
24212         * @param {Roo.bootstrap.CheckBox} this This input
24213         * @param {Boolean} checked The new checked value
24214         */
24215        check : true,
24216        /**
24217         * @event click
24218         * Fires when the element is click.
24219         * @param {Roo.bootstrap.CheckBox} this This input
24220         */
24221        click : true
24222     });
24223     
24224 };
24225
24226 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24227   
24228     inputType: 'checkbox',
24229     inputValue: 1,
24230     valueOff: 0,
24231     boxLabel: false,
24232     checked: false,
24233     weight : false,
24234     inline: false,
24235     tooltip : '',
24236     
24237     // checkbox success does not make any sense really.. 
24238     invalidClass : "",
24239     validClass : "",
24240     
24241     
24242     getAutoCreate : function()
24243     {
24244         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24245         
24246         var id = Roo.id();
24247         
24248         var cfg = {};
24249         
24250         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24251         
24252         if(this.inline){
24253             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24254         }
24255         
24256         var input =  {
24257             tag: 'input',
24258             id : id,
24259             type : this.inputType,
24260             value : this.inputValue,
24261             cls : 'roo-' + this.inputType, //'form-box',
24262             placeholder : this.placeholder || ''
24263             
24264         };
24265         
24266         if(this.inputType != 'radio'){
24267             var hidden =  {
24268                 tag: 'input',
24269                 type : 'hidden',
24270                 cls : 'roo-hidden-value',
24271                 value : this.checked ? this.inputValue : this.valueOff
24272             };
24273         }
24274         
24275             
24276         if (this.weight) { // Validity check?
24277             cfg.cls += " " + this.inputType + "-" + this.weight;
24278         }
24279         
24280         if (this.disabled) {
24281             input.disabled=true;
24282         }
24283         
24284         if(this.checked){
24285             input.checked = this.checked;
24286         }
24287         
24288         if (this.name) {
24289             
24290             input.name = this.name;
24291             
24292             if(this.inputType != 'radio'){
24293                 hidden.name = this.name;
24294                 input.name = '_hidden_' + this.name;
24295             }
24296         }
24297         
24298         if (this.size) {
24299             input.cls += ' input-' + this.size;
24300         }
24301         
24302         var settings=this;
24303         
24304         ['xs','sm','md','lg'].map(function(size){
24305             if (settings[size]) {
24306                 cfg.cls += ' col-' + size + '-' + settings[size];
24307             }
24308         });
24309         
24310         var inputblock = input;
24311          
24312         if (this.before || this.after) {
24313             
24314             inputblock = {
24315                 cls : 'input-group',
24316                 cn :  [] 
24317             };
24318             
24319             if (this.before) {
24320                 inputblock.cn.push({
24321                     tag :'span',
24322                     cls : 'input-group-addon',
24323                     html : this.before
24324                 });
24325             }
24326             
24327             inputblock.cn.push(input);
24328             
24329             if(this.inputType != 'radio'){
24330                 inputblock.cn.push(hidden);
24331             }
24332             
24333             if (this.after) {
24334                 inputblock.cn.push({
24335                     tag :'span',
24336                     cls : 'input-group-addon',
24337                     html : this.after
24338                 });
24339             }
24340             
24341         }
24342         var boxLabelCfg = false;
24343         
24344         if(this.boxLabel){
24345            
24346             boxLabelCfg = {
24347                 tag: 'label',
24348                 //'for': id, // box label is handled by onclick - so no for...
24349                 cls: 'box-label',
24350                 html: this.boxLabel
24351             };
24352             if(this.tooltip){
24353                 boxLabelCfg.tooltip = this.tooltip;
24354             }
24355              
24356         }
24357         
24358         
24359         if (align ==='left' && this.fieldLabel.length) {
24360 //                Roo.log("left and has label");
24361             cfg.cn = [
24362                 {
24363                     tag: 'label',
24364                     'for' :  id,
24365                     cls : 'control-label',
24366                     html : this.fieldLabel
24367                 },
24368                 {
24369                     cls : "", 
24370                     cn: [
24371                         inputblock
24372                     ]
24373                 }
24374             ];
24375             
24376             if (boxLabelCfg) {
24377                 cfg.cn[1].cn.push(boxLabelCfg);
24378             }
24379             
24380             if(this.labelWidth > 12){
24381                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24382             }
24383             
24384             if(this.labelWidth < 13 && this.labelmd == 0){
24385                 this.labelmd = this.labelWidth;
24386             }
24387             
24388             if(this.labellg > 0){
24389                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24390                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24391             }
24392             
24393             if(this.labelmd > 0){
24394                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24395                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24396             }
24397             
24398             if(this.labelsm > 0){
24399                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24400                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24401             }
24402             
24403             if(this.labelxs > 0){
24404                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24405                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24406             }
24407             
24408         } else if ( this.fieldLabel.length) {
24409 //                Roo.log(" label");
24410                 cfg.cn = [
24411                    
24412                     {
24413                         tag: this.boxLabel ? 'span' : 'label',
24414                         'for': id,
24415                         cls: 'control-label box-input-label',
24416                         //cls : 'input-group-addon',
24417                         html : this.fieldLabel
24418                     },
24419                     
24420                     inputblock
24421                     
24422                 ];
24423                 if (boxLabelCfg) {
24424                     cfg.cn.push(boxLabelCfg);
24425                 }
24426
24427         } else {
24428             
24429 //                Roo.log(" no label && no align");
24430                 cfg.cn = [  inputblock ] ;
24431                 if (boxLabelCfg) {
24432                     cfg.cn.push(boxLabelCfg);
24433                 }
24434
24435                 
24436         }
24437         
24438        
24439         
24440         if(this.inputType != 'radio'){
24441             cfg.cn.push(hidden);
24442         }
24443         
24444         return cfg;
24445         
24446     },
24447     
24448     /**
24449      * return the real input element.
24450      */
24451     inputEl: function ()
24452     {
24453         return this.el.select('input.roo-' + this.inputType,true).first();
24454     },
24455     hiddenEl: function ()
24456     {
24457         return this.el.select('input.roo-hidden-value',true).first();
24458     },
24459     
24460     labelEl: function()
24461     {
24462         return this.el.select('label.control-label',true).first();
24463     },
24464     /* depricated... */
24465     
24466     label: function()
24467     {
24468         return this.labelEl();
24469     },
24470     
24471     boxLabelEl: function()
24472     {
24473         return this.el.select('label.box-label',true).first();
24474     },
24475     
24476     initEvents : function()
24477     {
24478 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24479         
24480         this.inputEl().on('click', this.onClick,  this);
24481         
24482         if (this.boxLabel) { 
24483             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24484         }
24485         
24486         this.startValue = this.getValue();
24487         
24488         if(this.groupId){
24489             Roo.bootstrap.CheckBox.register(this);
24490         }
24491     },
24492     
24493     onClick : function(e)
24494     {   
24495         if(this.fireEvent('click', this, e) !== false){
24496             this.setChecked(!this.checked);
24497         }
24498         
24499     },
24500     
24501     setChecked : function(state,suppressEvent)
24502     {
24503         this.startValue = this.getValue();
24504
24505         if(this.inputType == 'radio'){
24506             
24507             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24508                 e.dom.checked = false;
24509             });
24510             
24511             this.inputEl().dom.checked = true;
24512             
24513             this.inputEl().dom.value = this.inputValue;
24514             
24515             if(suppressEvent !== true){
24516                 this.fireEvent('check', this, true);
24517             }
24518             
24519             this.validate();
24520             
24521             return;
24522         }
24523         
24524         this.checked = state;
24525         
24526         this.inputEl().dom.checked = state;
24527         
24528         
24529         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24530         
24531         if(suppressEvent !== true){
24532             this.fireEvent('check', this, state);
24533         }
24534         
24535         this.validate();
24536     },
24537     
24538     getValue : function()
24539     {
24540         if(this.inputType == 'radio'){
24541             return this.getGroupValue();
24542         }
24543         
24544         return this.hiddenEl().dom.value;
24545         
24546     },
24547     
24548     getGroupValue : function()
24549     {
24550         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24551             return '';
24552         }
24553         
24554         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24555     },
24556     
24557     setValue : function(v,suppressEvent)
24558     {
24559         if(this.inputType == 'radio'){
24560             this.setGroupValue(v, suppressEvent);
24561             return;
24562         }
24563         
24564         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24565         
24566         this.validate();
24567     },
24568     
24569     setGroupValue : function(v, suppressEvent)
24570     {
24571         this.startValue = this.getValue();
24572         
24573         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24574             e.dom.checked = false;
24575             
24576             if(e.dom.value == v){
24577                 e.dom.checked = true;
24578             }
24579         });
24580         
24581         if(suppressEvent !== true){
24582             this.fireEvent('check', this, true);
24583         }
24584
24585         this.validate();
24586         
24587         return;
24588     },
24589     
24590     validate : function()
24591     {
24592         if(this.getVisibilityEl().hasClass('hidden')){
24593             return true;
24594         }
24595         
24596         if(
24597                 this.disabled || 
24598                 (this.inputType == 'radio' && this.validateRadio()) ||
24599                 (this.inputType == 'checkbox' && this.validateCheckbox())
24600         ){
24601             this.markValid();
24602             return true;
24603         }
24604         
24605         this.markInvalid();
24606         return false;
24607     },
24608     
24609     validateRadio : function()
24610     {
24611         if(this.getVisibilityEl().hasClass('hidden')){
24612             return true;
24613         }
24614         
24615         if(this.allowBlank){
24616             return true;
24617         }
24618         
24619         var valid = false;
24620         
24621         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24622             if(!e.dom.checked){
24623                 return;
24624             }
24625             
24626             valid = true;
24627             
24628             return false;
24629         });
24630         
24631         return valid;
24632     },
24633     
24634     validateCheckbox : function()
24635     {
24636         if(!this.groupId){
24637             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24638             //return (this.getValue() == this.inputValue) ? true : false;
24639         }
24640         
24641         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24642         
24643         if(!group){
24644             return false;
24645         }
24646         
24647         var r = false;
24648         
24649         for(var i in group){
24650             if(group[i].el.isVisible(true)){
24651                 r = false;
24652                 break;
24653             }
24654             
24655             r = true;
24656         }
24657         
24658         for(var i in group){
24659             if(r){
24660                 break;
24661             }
24662             
24663             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24664         }
24665         
24666         return r;
24667     },
24668     
24669     /**
24670      * Mark this field as valid
24671      */
24672     markValid : function()
24673     {
24674         var _this = this;
24675         
24676         this.fireEvent('valid', this);
24677         
24678         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24679         
24680         if(this.groupId){
24681             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24682         }
24683         
24684         if(label){
24685             label.markValid();
24686         }
24687
24688         if(this.inputType == 'radio'){
24689             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24690                 var fg = e.findParent('.form-group', false, true);
24691                 if (Roo.bootstrap.version == 3) {
24692                     fg.removeClass([_this.invalidClass, _this.validClass]);
24693                     fg.addClass(_this.validClass);
24694                 } else {
24695                     fg.removeClass(['is-valid', 'is-invalid']);
24696                     fg.addClass('is-valid');
24697                 }
24698             });
24699             
24700             return;
24701         }
24702
24703         if(!this.groupId){
24704             var fg = this.el.findParent('.form-group', false, true);
24705             if (Roo.bootstrap.version == 3) {
24706                 fg.removeClass([this.invalidClass, this.validClass]);
24707                 fg.addClass(this.validClass);
24708             } else {
24709                 fg.removeClass(['is-valid', 'is-invalid']);
24710                 fg.addClass('is-valid');
24711             }
24712             return;
24713         }
24714         
24715         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24716         
24717         if(!group){
24718             return;
24719         }
24720         
24721         for(var i in group){
24722             var fg = group[i].el.findParent('.form-group', false, true);
24723             if (Roo.bootstrap.version == 3) {
24724                 fg.removeClass([this.invalidClass, this.validClass]);
24725                 fg.addClass(this.validClass);
24726             } else {
24727                 fg.removeClass(['is-valid', 'is-invalid']);
24728                 fg.addClass('is-valid');
24729             }
24730         }
24731     },
24732     
24733      /**
24734      * Mark this field as invalid
24735      * @param {String} msg The validation message
24736      */
24737     markInvalid : function(msg)
24738     {
24739         if(this.allowBlank){
24740             return;
24741         }
24742         
24743         var _this = this;
24744         
24745         this.fireEvent('invalid', this, msg);
24746         
24747         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24748         
24749         if(this.groupId){
24750             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24751         }
24752         
24753         if(label){
24754             label.markInvalid();
24755         }
24756             
24757         if(this.inputType == 'radio'){
24758             
24759             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24760                 var fg = e.findParent('.form-group', false, true);
24761                 if (Roo.bootstrap.version == 3) {
24762                     fg.removeClass([_this.invalidClass, _this.validClass]);
24763                     fg.addClass(_this.invalidClass);
24764                 } else {
24765                     fg.removeClass(['is-invalid', 'is-valid']);
24766                     fg.addClass('is-invalid');
24767                 }
24768             });
24769             
24770             return;
24771         }
24772         
24773         if(!this.groupId){
24774             var fg = this.el.findParent('.form-group', false, true);
24775             if (Roo.bootstrap.version == 3) {
24776                 fg.removeClass([_this.invalidClass, _this.validClass]);
24777                 fg.addClass(_this.invalidClass);
24778             } else {
24779                 fg.removeClass(['is-invalid', 'is-valid']);
24780                 fg.addClass('is-invalid');
24781             }
24782             return;
24783         }
24784         
24785         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24786         
24787         if(!group){
24788             return;
24789         }
24790         
24791         for(var i in group){
24792             var fg = group[i].el.findParent('.form-group', false, true);
24793             if (Roo.bootstrap.version == 3) {
24794                 fg.removeClass([_this.invalidClass, _this.validClass]);
24795                 fg.addClass(_this.invalidClass);
24796             } else {
24797                 fg.removeClass(['is-invalid', 'is-valid']);
24798                 fg.addClass('is-invalid');
24799             }
24800         }
24801         
24802     },
24803     
24804     clearInvalid : function()
24805     {
24806         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24807         
24808         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24809         
24810         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24811         
24812         if (label && label.iconEl) {
24813             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24814             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24815         }
24816     },
24817     
24818     disable : function()
24819     {
24820         if(this.inputType != 'radio'){
24821             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24822             return;
24823         }
24824         
24825         var _this = this;
24826         
24827         if(this.rendered){
24828             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24829                 _this.getActionEl().addClass(this.disabledClass);
24830                 e.dom.disabled = true;
24831             });
24832         }
24833         
24834         this.disabled = true;
24835         this.fireEvent("disable", this);
24836         return this;
24837     },
24838
24839     enable : function()
24840     {
24841         if(this.inputType != 'radio'){
24842             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24843             return;
24844         }
24845         
24846         var _this = this;
24847         
24848         if(this.rendered){
24849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24850                 _this.getActionEl().removeClass(this.disabledClass);
24851                 e.dom.disabled = false;
24852             });
24853         }
24854         
24855         this.disabled = false;
24856         this.fireEvent("enable", this);
24857         return this;
24858     },
24859     
24860     setBoxLabel : function(v)
24861     {
24862         this.boxLabel = v;
24863         
24864         if(this.rendered){
24865             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24866         }
24867     }
24868
24869 });
24870
24871 Roo.apply(Roo.bootstrap.CheckBox, {
24872     
24873     groups: {},
24874     
24875      /**
24876     * register a CheckBox Group
24877     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24878     */
24879     register : function(checkbox)
24880     {
24881         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24882             this.groups[checkbox.groupId] = {};
24883         }
24884         
24885         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24886             return;
24887         }
24888         
24889         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24890         
24891     },
24892     /**
24893     * fetch a CheckBox Group based on the group ID
24894     * @param {string} the group ID
24895     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24896     */
24897     get: function(groupId) {
24898         if (typeof(this.groups[groupId]) == 'undefined') {
24899             return false;
24900         }
24901         
24902         return this.groups[groupId] ;
24903     }
24904     
24905     
24906 });
24907 /*
24908  * - LGPL
24909  *
24910  * RadioItem
24911  * 
24912  */
24913
24914 /**
24915  * @class Roo.bootstrap.Radio
24916  * @extends Roo.bootstrap.Component
24917  * Bootstrap Radio class
24918  * @cfg {String} boxLabel - the label associated
24919  * @cfg {String} value - the value of radio
24920  * 
24921  * @constructor
24922  * Create a new Radio
24923  * @param {Object} config The config object
24924  */
24925 Roo.bootstrap.Radio = function(config){
24926     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24927     
24928 };
24929
24930 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24931     
24932     boxLabel : '',
24933     
24934     value : '',
24935     
24936     getAutoCreate : function()
24937     {
24938         var cfg = {
24939             tag : 'div',
24940             cls : 'form-group radio',
24941             cn : [
24942                 {
24943                     tag : 'label',
24944                     cls : 'box-label',
24945                     html : this.boxLabel
24946                 }
24947             ]
24948         };
24949         
24950         return cfg;
24951     },
24952     
24953     initEvents : function() 
24954     {
24955         this.parent().register(this);
24956         
24957         this.el.on('click', this.onClick, this);
24958         
24959     },
24960     
24961     onClick : function(e)
24962     {
24963         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24964             this.setChecked(true);
24965         }
24966     },
24967     
24968     setChecked : function(state, suppressEvent)
24969     {
24970         this.parent().setValue(this.value, suppressEvent);
24971         
24972     },
24973     
24974     setBoxLabel : function(v)
24975     {
24976         this.boxLabel = v;
24977         
24978         if(this.rendered){
24979             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24980         }
24981     }
24982     
24983 });
24984  
24985
24986  /*
24987  * - LGPL
24988  *
24989  * Input
24990  * 
24991  */
24992
24993 /**
24994  * @class Roo.bootstrap.SecurePass
24995  * @extends Roo.bootstrap.Input
24996  * Bootstrap SecurePass class
24997  *
24998  * 
24999  * @constructor
25000  * Create a new SecurePass
25001  * @param {Object} config The config object
25002  */
25003  
25004 Roo.bootstrap.SecurePass = function (config) {
25005     // these go here, so the translation tool can replace them..
25006     this.errors = {
25007         PwdEmpty: "Please type a password, and then retype it to confirm.",
25008         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25009         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25010         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25011         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25012         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25013         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25014         TooWeak: "Your password is Too Weak."
25015     },
25016     this.meterLabel = "Password strength:";
25017     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25018     this.meterClass = [
25019         "roo-password-meter-tooweak", 
25020         "roo-password-meter-weak", 
25021         "roo-password-meter-medium", 
25022         "roo-password-meter-strong", 
25023         "roo-password-meter-grey"
25024     ];
25025     
25026     this.errors = {};
25027     
25028     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25029 }
25030
25031 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25032     /**
25033      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25034      * {
25035      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25036      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25037      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25038      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25039      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25040      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25041      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25042      * })
25043      */
25044     // private
25045     
25046     meterWidth: 300,
25047     errorMsg :'',    
25048     errors: false,
25049     imageRoot: '/',
25050     /**
25051      * @cfg {String/Object} Label for the strength meter (defaults to
25052      * 'Password strength:')
25053      */
25054     // private
25055     meterLabel: '',
25056     /**
25057      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25058      * ['Weak', 'Medium', 'Strong'])
25059      */
25060     // private    
25061     pwdStrengths: false,    
25062     // private
25063     strength: 0,
25064     // private
25065     _lastPwd: null,
25066     // private
25067     kCapitalLetter: 0,
25068     kSmallLetter: 1,
25069     kDigit: 2,
25070     kPunctuation: 3,
25071     
25072     insecure: false,
25073     // private
25074     initEvents: function ()
25075     {
25076         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25077
25078         if (this.el.is('input[type=password]') && Roo.isSafari) {
25079             this.el.on('keydown', this.SafariOnKeyDown, this);
25080         }
25081
25082         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25083     },
25084     // private
25085     onRender: function (ct, position)
25086     {
25087         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25088         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25089         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25090
25091         this.trigger.createChild({
25092                    cn: [
25093                     {
25094                     //id: 'PwdMeter',
25095                     tag: 'div',
25096                     cls: 'roo-password-meter-grey col-xs-12',
25097                     style: {
25098                         //width: 0,
25099                         //width: this.meterWidth + 'px'                                                
25100                         }
25101                     },
25102                     {                            
25103                          cls: 'roo-password-meter-text'                          
25104                     }
25105                 ]            
25106         });
25107
25108          
25109         if (this.hideTrigger) {
25110             this.trigger.setDisplayed(false);
25111         }
25112         this.setSize(this.width || '', this.height || '');
25113     },
25114     // private
25115     onDestroy: function ()
25116     {
25117         if (this.trigger) {
25118             this.trigger.removeAllListeners();
25119             this.trigger.remove();
25120         }
25121         if (this.wrap) {
25122             this.wrap.remove();
25123         }
25124         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25125     },
25126     // private
25127     checkStrength: function ()
25128     {
25129         var pwd = this.inputEl().getValue();
25130         if (pwd == this._lastPwd) {
25131             return;
25132         }
25133
25134         var strength;
25135         if (this.ClientSideStrongPassword(pwd)) {
25136             strength = 3;
25137         } else if (this.ClientSideMediumPassword(pwd)) {
25138             strength = 2;
25139         } else if (this.ClientSideWeakPassword(pwd)) {
25140             strength = 1;
25141         } else {
25142             strength = 0;
25143         }
25144         
25145         Roo.log('strength1: ' + strength);
25146         
25147         //var pm = this.trigger.child('div/div/div').dom;
25148         var pm = this.trigger.child('div/div');
25149         pm.removeClass(this.meterClass);
25150         pm.addClass(this.meterClass[strength]);
25151                 
25152         
25153         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25154                 
25155         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25156         
25157         this._lastPwd = pwd;
25158     },
25159     reset: function ()
25160     {
25161         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25162         
25163         this._lastPwd = '';
25164         
25165         var pm = this.trigger.child('div/div');
25166         pm.removeClass(this.meterClass);
25167         pm.addClass('roo-password-meter-grey');        
25168         
25169         
25170         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25171         
25172         pt.innerHTML = '';
25173         this.inputEl().dom.type='password';
25174     },
25175     // private
25176     validateValue: function (value)
25177     {
25178         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25179             return false;
25180         }
25181         if (value.length == 0) {
25182             if (this.allowBlank) {
25183                 this.clearInvalid();
25184                 return true;
25185             }
25186
25187             this.markInvalid(this.errors.PwdEmpty);
25188             this.errorMsg = this.errors.PwdEmpty;
25189             return false;
25190         }
25191         
25192         if(this.insecure){
25193             return true;
25194         }
25195         
25196         if (!value.match(/[\x21-\x7e]+/)) {
25197             this.markInvalid(this.errors.PwdBadChar);
25198             this.errorMsg = this.errors.PwdBadChar;
25199             return false;
25200         }
25201         if (value.length < 6) {
25202             this.markInvalid(this.errors.PwdShort);
25203             this.errorMsg = this.errors.PwdShort;
25204             return false;
25205         }
25206         if (value.length > 16) {
25207             this.markInvalid(this.errors.PwdLong);
25208             this.errorMsg = this.errors.PwdLong;
25209             return false;
25210         }
25211         var strength;
25212         if (this.ClientSideStrongPassword(value)) {
25213             strength = 3;
25214         } else if (this.ClientSideMediumPassword(value)) {
25215             strength = 2;
25216         } else if (this.ClientSideWeakPassword(value)) {
25217             strength = 1;
25218         } else {
25219             strength = 0;
25220         }
25221
25222         
25223         if (strength < 2) {
25224             //this.markInvalid(this.errors.TooWeak);
25225             this.errorMsg = this.errors.TooWeak;
25226             //return false;
25227         }
25228         
25229         
25230         console.log('strength2: ' + strength);
25231         
25232         //var pm = this.trigger.child('div/div/div').dom;
25233         
25234         var pm = this.trigger.child('div/div');
25235         pm.removeClass(this.meterClass);
25236         pm.addClass(this.meterClass[strength]);
25237                 
25238         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25239                 
25240         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25241         
25242         this.errorMsg = ''; 
25243         return true;
25244     },
25245     // private
25246     CharacterSetChecks: function (type)
25247     {
25248         this.type = type;
25249         this.fResult = false;
25250     },
25251     // private
25252     isctype: function (character, type)
25253     {
25254         switch (type) {  
25255             case this.kCapitalLetter:
25256                 if (character >= 'A' && character <= 'Z') {
25257                     return true;
25258                 }
25259                 break;
25260             
25261             case this.kSmallLetter:
25262                 if (character >= 'a' && character <= 'z') {
25263                     return true;
25264                 }
25265                 break;
25266             
25267             case this.kDigit:
25268                 if (character >= '0' && character <= '9') {
25269                     return true;
25270                 }
25271                 break;
25272             
25273             case this.kPunctuation:
25274                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25275                     return true;
25276                 }
25277                 break;
25278             
25279             default:
25280                 return false;
25281         }
25282
25283     },
25284     // private
25285     IsLongEnough: function (pwd, size)
25286     {
25287         return !(pwd == null || isNaN(size) || pwd.length < size);
25288     },
25289     // private
25290     SpansEnoughCharacterSets: function (word, nb)
25291     {
25292         if (!this.IsLongEnough(word, nb))
25293         {
25294             return false;
25295         }
25296
25297         var characterSetChecks = new Array(
25298             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25299             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25300         );
25301         
25302         for (var index = 0; index < word.length; ++index) {
25303             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25304                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25305                     characterSetChecks[nCharSet].fResult = true;
25306                     break;
25307                 }
25308             }
25309         }
25310
25311         var nCharSets = 0;
25312         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25313             if (characterSetChecks[nCharSet].fResult) {
25314                 ++nCharSets;
25315             }
25316         }
25317
25318         if (nCharSets < nb) {
25319             return false;
25320         }
25321         return true;
25322     },
25323     // private
25324     ClientSideStrongPassword: function (pwd)
25325     {
25326         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25327     },
25328     // private
25329     ClientSideMediumPassword: function (pwd)
25330     {
25331         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25332     },
25333     // private
25334     ClientSideWeakPassword: function (pwd)
25335     {
25336         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25337     }
25338           
25339 })//<script type="text/javascript">
25340
25341 /*
25342  * Based  Ext JS Library 1.1.1
25343  * Copyright(c) 2006-2007, Ext JS, LLC.
25344  * LGPL
25345  *
25346  */
25347  
25348 /**
25349  * @class Roo.HtmlEditorCore
25350  * @extends Roo.Component
25351  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25352  *
25353  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25354  */
25355
25356 Roo.HtmlEditorCore = function(config){
25357     
25358     
25359     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25360     
25361     
25362     this.addEvents({
25363         /**
25364          * @event initialize
25365          * Fires when the editor is fully initialized (including the iframe)
25366          * @param {Roo.HtmlEditorCore} this
25367          */
25368         initialize: true,
25369         /**
25370          * @event activate
25371          * Fires when the editor is first receives the focus. Any insertion must wait
25372          * until after this event.
25373          * @param {Roo.HtmlEditorCore} this
25374          */
25375         activate: true,
25376          /**
25377          * @event beforesync
25378          * Fires before the textarea is updated with content from the editor iframe. Return false
25379          * to cancel the sync.
25380          * @param {Roo.HtmlEditorCore} this
25381          * @param {String} html
25382          */
25383         beforesync: true,
25384          /**
25385          * @event beforepush
25386          * Fires before the iframe editor is updated with content from the textarea. Return false
25387          * to cancel the push.
25388          * @param {Roo.HtmlEditorCore} this
25389          * @param {String} html
25390          */
25391         beforepush: true,
25392          /**
25393          * @event sync
25394          * Fires when the textarea is updated with content from the editor iframe.
25395          * @param {Roo.HtmlEditorCore} this
25396          * @param {String} html
25397          */
25398         sync: true,
25399          /**
25400          * @event push
25401          * Fires when the iframe editor is updated with content from the textarea.
25402          * @param {Roo.HtmlEditorCore} this
25403          * @param {String} html
25404          */
25405         push: true,
25406         
25407         /**
25408          * @event editorevent
25409          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25410          * @param {Roo.HtmlEditorCore} this
25411          */
25412         editorevent: true
25413         
25414     });
25415     
25416     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25417     
25418     // defaults : white / black...
25419     this.applyBlacklists();
25420     
25421     
25422     
25423 };
25424
25425
25426 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25427
25428
25429      /**
25430      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25431      */
25432     
25433     owner : false,
25434     
25435      /**
25436      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25437      *                        Roo.resizable.
25438      */
25439     resizable : false,
25440      /**
25441      * @cfg {Number} height (in pixels)
25442      */   
25443     height: 300,
25444    /**
25445      * @cfg {Number} width (in pixels)
25446      */   
25447     width: 500,
25448     
25449     /**
25450      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25451      * 
25452      */
25453     stylesheets: false,
25454     
25455     // id of frame..
25456     frameId: false,
25457     
25458     // private properties
25459     validationEvent : false,
25460     deferHeight: true,
25461     initialized : false,
25462     activated : false,
25463     sourceEditMode : false,
25464     onFocus : Roo.emptyFn,
25465     iframePad:3,
25466     hideMode:'offsets',
25467     
25468     clearUp: true,
25469     
25470     // blacklist + whitelisted elements..
25471     black: false,
25472     white: false,
25473      
25474     bodyCls : '',
25475
25476     /**
25477      * Protected method that will not generally be called directly. It
25478      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25479      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25480      */
25481     getDocMarkup : function(){
25482         // body styles..
25483         var st = '';
25484         
25485         // inherit styels from page...?? 
25486         if (this.stylesheets === false) {
25487             
25488             Roo.get(document.head).select('style').each(function(node) {
25489                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25490             });
25491             
25492             Roo.get(document.head).select('link').each(function(node) { 
25493                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25494             });
25495             
25496         } else if (!this.stylesheets.length) {
25497                 // simple..
25498                 st = '<style type="text/css">' +
25499                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25500                    '</style>';
25501         } else {
25502             for (var i in this.stylesheets) { 
25503                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25504             }
25505             
25506         }
25507         
25508         st +=  '<style type="text/css">' +
25509             'IMG { cursor: pointer } ' +
25510         '</style>';
25511
25512         var cls = 'roo-htmleditor-body';
25513         
25514         if(this.bodyCls.length){
25515             cls += ' ' + this.bodyCls;
25516         }
25517         
25518         return '<html><head>' + st  +
25519             //<style type="text/css">' +
25520             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25521             //'</style>' +
25522             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25523     },
25524
25525     // private
25526     onRender : function(ct, position)
25527     {
25528         var _t = this;
25529         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25530         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25531         
25532         
25533         this.el.dom.style.border = '0 none';
25534         this.el.dom.setAttribute('tabIndex', -1);
25535         this.el.addClass('x-hidden hide');
25536         
25537         
25538         
25539         if(Roo.isIE){ // fix IE 1px bogus margin
25540             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25541         }
25542        
25543         
25544         this.frameId = Roo.id();
25545         
25546          
25547         
25548         var iframe = this.owner.wrap.createChild({
25549             tag: 'iframe',
25550             cls: 'form-control', // bootstrap..
25551             id: this.frameId,
25552             name: this.frameId,
25553             frameBorder : 'no',
25554             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25555         }, this.el
25556         );
25557         
25558         
25559         this.iframe = iframe.dom;
25560
25561          this.assignDocWin();
25562         
25563         this.doc.designMode = 'on';
25564        
25565         this.doc.open();
25566         this.doc.write(this.getDocMarkup());
25567         this.doc.close();
25568
25569         
25570         var task = { // must defer to wait for browser to be ready
25571             run : function(){
25572                 //console.log("run task?" + this.doc.readyState);
25573                 this.assignDocWin();
25574                 if(this.doc.body || this.doc.readyState == 'complete'){
25575                     try {
25576                         this.doc.designMode="on";
25577                     } catch (e) {
25578                         return;
25579                     }
25580                     Roo.TaskMgr.stop(task);
25581                     this.initEditor.defer(10, this);
25582                 }
25583             },
25584             interval : 10,
25585             duration: 10000,
25586             scope: this
25587         };
25588         Roo.TaskMgr.start(task);
25589
25590     },
25591
25592     // private
25593     onResize : function(w, h)
25594     {
25595          Roo.log('resize: ' +w + ',' + h );
25596         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25597         if(!this.iframe){
25598             return;
25599         }
25600         if(typeof w == 'number'){
25601             
25602             this.iframe.style.width = w + 'px';
25603         }
25604         if(typeof h == 'number'){
25605             
25606             this.iframe.style.height = h + 'px';
25607             if(this.doc){
25608                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25609             }
25610         }
25611         
25612     },
25613
25614     /**
25615      * Toggles the editor between standard and source edit mode.
25616      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25617      */
25618     toggleSourceEdit : function(sourceEditMode){
25619         
25620         this.sourceEditMode = sourceEditMode === true;
25621         
25622         if(this.sourceEditMode){
25623  
25624             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25625             
25626         }else{
25627             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25628             //this.iframe.className = '';
25629             this.deferFocus();
25630         }
25631         //this.setSize(this.owner.wrap.getSize());
25632         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25633     },
25634
25635     
25636   
25637
25638     /**
25639      * Protected method that will not generally be called directly. If you need/want
25640      * custom HTML cleanup, this is the method you should override.
25641      * @param {String} html The HTML to be cleaned
25642      * return {String} The cleaned HTML
25643      */
25644     cleanHtml : function(html){
25645         html = String(html);
25646         if(html.length > 5){
25647             if(Roo.isSafari){ // strip safari nonsense
25648                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25649             }
25650         }
25651         if(html == '&nbsp;'){
25652             html = '';
25653         }
25654         return html;
25655     },
25656
25657     /**
25658      * HTML Editor -> Textarea
25659      * Protected method that will not generally be called directly. Syncs the contents
25660      * of the editor iframe with the textarea.
25661      */
25662     syncValue : function(){
25663         if(this.initialized){
25664             var bd = (this.doc.body || this.doc.documentElement);
25665             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25666             var html = bd.innerHTML;
25667             if(Roo.isSafari){
25668                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25669                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25670                 if(m && m[1]){
25671                     html = '<div style="'+m[0]+'">' + html + '</div>';
25672                 }
25673             }
25674             html = this.cleanHtml(html);
25675             // fix up the special chars.. normaly like back quotes in word...
25676             // however we do not want to do this with chinese..
25677             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25678                 
25679                 var cc = match.charCodeAt();
25680
25681                 // Get the character value, handling surrogate pairs
25682                 if (match.length == 2) {
25683                     // It's a surrogate pair, calculate the Unicode code point
25684                     var high = match.charCodeAt(0) - 0xD800;
25685                     var low  = match.charCodeAt(1) - 0xDC00;
25686                     cc = (high * 0x400) + low + 0x10000;
25687                 }  else if (
25688                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25689                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25690                     (cc >= 0xf900 && cc < 0xfb00 )
25691                 ) {
25692                         return match;
25693                 }  
25694          
25695                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25696                 return "&#" + cc + ";";
25697                 
25698                 
25699             });
25700             
25701             
25702              
25703             if(this.owner.fireEvent('beforesync', this, html) !== false){
25704                 this.el.dom.value = html;
25705                 this.owner.fireEvent('sync', this, html);
25706             }
25707         }
25708     },
25709
25710     /**
25711      * Protected method that will not generally be called directly. Pushes the value of the textarea
25712      * into the iframe editor.
25713      */
25714     pushValue : function(){
25715         if(this.initialized){
25716             var v = this.el.dom.value.trim();
25717             
25718 //            if(v.length < 1){
25719 //                v = '&#160;';
25720 //            }
25721             
25722             if(this.owner.fireEvent('beforepush', this, v) !== false){
25723                 var d = (this.doc.body || this.doc.documentElement);
25724                 d.innerHTML = v;
25725                 this.cleanUpPaste();
25726                 this.el.dom.value = d.innerHTML;
25727                 this.owner.fireEvent('push', this, v);
25728             }
25729         }
25730     },
25731
25732     // private
25733     deferFocus : function(){
25734         this.focus.defer(10, this);
25735     },
25736
25737     // doc'ed in Field
25738     focus : function(){
25739         if(this.win && !this.sourceEditMode){
25740             this.win.focus();
25741         }else{
25742             this.el.focus();
25743         }
25744     },
25745     
25746     assignDocWin: function()
25747     {
25748         var iframe = this.iframe;
25749         
25750          if(Roo.isIE){
25751             this.doc = iframe.contentWindow.document;
25752             this.win = iframe.contentWindow;
25753         } else {
25754 //            if (!Roo.get(this.frameId)) {
25755 //                return;
25756 //            }
25757 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25758 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25759             
25760             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25761                 return;
25762             }
25763             
25764             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25765             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25766         }
25767     },
25768     
25769     // private
25770     initEditor : function(){
25771         //console.log("INIT EDITOR");
25772         this.assignDocWin();
25773         
25774         
25775         
25776         this.doc.designMode="on";
25777         this.doc.open();
25778         this.doc.write(this.getDocMarkup());
25779         this.doc.close();
25780         
25781         var dbody = (this.doc.body || this.doc.documentElement);
25782         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25783         // this copies styles from the containing element into thsi one..
25784         // not sure why we need all of this..
25785         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25786         
25787         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25788         //ss['background-attachment'] = 'fixed'; // w3c
25789         dbody.bgProperties = 'fixed'; // ie
25790         //Roo.DomHelper.applyStyles(dbody, ss);
25791         Roo.EventManager.on(this.doc, {
25792             //'mousedown': this.onEditorEvent,
25793             'mouseup': this.onEditorEvent,
25794             'dblclick': this.onEditorEvent,
25795             'click': this.onEditorEvent,
25796             'keyup': this.onEditorEvent,
25797             buffer:100,
25798             scope: this
25799         });
25800         if(Roo.isGecko){
25801             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25802         }
25803         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25804             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25805         }
25806         this.initialized = true;
25807
25808         this.owner.fireEvent('initialize', this);
25809         this.pushValue();
25810     },
25811
25812     // private
25813     onDestroy : function(){
25814         
25815         
25816         
25817         if(this.rendered){
25818             
25819             //for (var i =0; i < this.toolbars.length;i++) {
25820             //    // fixme - ask toolbars for heights?
25821             //    this.toolbars[i].onDestroy();
25822            // }
25823             
25824             //this.wrap.dom.innerHTML = '';
25825             //this.wrap.remove();
25826         }
25827     },
25828
25829     // private
25830     onFirstFocus : function(){
25831         
25832         this.assignDocWin();
25833         
25834         
25835         this.activated = true;
25836          
25837     
25838         if(Roo.isGecko){ // prevent silly gecko errors
25839             this.win.focus();
25840             var s = this.win.getSelection();
25841             if(!s.focusNode || s.focusNode.nodeType != 3){
25842                 var r = s.getRangeAt(0);
25843                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25844                 r.collapse(true);
25845                 this.deferFocus();
25846             }
25847             try{
25848                 this.execCmd('useCSS', true);
25849                 this.execCmd('styleWithCSS', false);
25850             }catch(e){}
25851         }
25852         this.owner.fireEvent('activate', this);
25853     },
25854
25855     // private
25856     adjustFont: function(btn){
25857         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25858         //if(Roo.isSafari){ // safari
25859         //    adjust *= 2;
25860        // }
25861         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25862         if(Roo.isSafari){ // safari
25863             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25864             v =  (v < 10) ? 10 : v;
25865             v =  (v > 48) ? 48 : v;
25866             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25867             
25868         }
25869         
25870         
25871         v = Math.max(1, v+adjust);
25872         
25873         this.execCmd('FontSize', v  );
25874     },
25875
25876     onEditorEvent : function(e)
25877     {
25878         this.owner.fireEvent('editorevent', this, e);
25879       //  this.updateToolbar();
25880         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25881     },
25882
25883     insertTag : function(tg)
25884     {
25885         // could be a bit smarter... -> wrap the current selected tRoo..
25886         if (tg.toLowerCase() == 'span' ||
25887             tg.toLowerCase() == 'code' ||
25888             tg.toLowerCase() == 'sup' ||
25889             tg.toLowerCase() == 'sub' 
25890             ) {
25891             
25892             range = this.createRange(this.getSelection());
25893             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25894             wrappingNode.appendChild(range.extractContents());
25895             range.insertNode(wrappingNode);
25896
25897             return;
25898             
25899             
25900             
25901         }
25902         this.execCmd("formatblock",   tg);
25903         
25904     },
25905     
25906     insertText : function(txt)
25907     {
25908         
25909         
25910         var range = this.createRange();
25911         range.deleteContents();
25912                //alert(Sender.getAttribute('label'));
25913                
25914         range.insertNode(this.doc.createTextNode(txt));
25915     } ,
25916     
25917      
25918
25919     /**
25920      * Executes a Midas editor command on the editor document and performs necessary focus and
25921      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25922      * @param {String} cmd The Midas command
25923      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25924      */
25925     relayCmd : function(cmd, value){
25926         this.win.focus();
25927         this.execCmd(cmd, value);
25928         this.owner.fireEvent('editorevent', this);
25929         //this.updateToolbar();
25930         this.owner.deferFocus();
25931     },
25932
25933     /**
25934      * Executes a Midas editor command directly on the editor document.
25935      * For visual commands, you should use {@link #relayCmd} instead.
25936      * <b>This should only be called after the editor is initialized.</b>
25937      * @param {String} cmd The Midas command
25938      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25939      */
25940     execCmd : function(cmd, value){
25941         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25942         this.syncValue();
25943     },
25944  
25945  
25946    
25947     /**
25948      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25949      * to insert tRoo.
25950      * @param {String} text | dom node.. 
25951      */
25952     insertAtCursor : function(text)
25953     {
25954         
25955         if(!this.activated){
25956             return;
25957         }
25958         /*
25959         if(Roo.isIE){
25960             this.win.focus();
25961             var r = this.doc.selection.createRange();
25962             if(r){
25963                 r.collapse(true);
25964                 r.pasteHTML(text);
25965                 this.syncValue();
25966                 this.deferFocus();
25967             
25968             }
25969             return;
25970         }
25971         */
25972         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25973             this.win.focus();
25974             
25975             
25976             // from jquery ui (MIT licenced)
25977             var range, node;
25978             var win = this.win;
25979             
25980             if (win.getSelection && win.getSelection().getRangeAt) {
25981                 range = win.getSelection().getRangeAt(0);
25982                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25983                 range.insertNode(node);
25984             } else if (win.document.selection && win.document.selection.createRange) {
25985                 // no firefox support
25986                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25987                 win.document.selection.createRange().pasteHTML(txt);
25988             } else {
25989                 // no firefox support
25990                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25991                 this.execCmd('InsertHTML', txt);
25992             } 
25993             
25994             this.syncValue();
25995             
25996             this.deferFocus();
25997         }
25998     },
25999  // private
26000     mozKeyPress : function(e){
26001         if(e.ctrlKey){
26002             var c = e.getCharCode(), cmd;
26003           
26004             if(c > 0){
26005                 c = String.fromCharCode(c).toLowerCase();
26006                 switch(c){
26007                     case 'b':
26008                         cmd = 'bold';
26009                         break;
26010                     case 'i':
26011                         cmd = 'italic';
26012                         break;
26013                     
26014                     case 'u':
26015                         cmd = 'underline';
26016                         break;
26017                     
26018                     case 'v':
26019                         this.cleanUpPaste.defer(100, this);
26020                         return;
26021                         
26022                 }
26023                 if(cmd){
26024                     this.win.focus();
26025                     this.execCmd(cmd);
26026                     this.deferFocus();
26027                     e.preventDefault();
26028                 }
26029                 
26030             }
26031         }
26032     },
26033
26034     // private
26035     fixKeys : function(){ // load time branching for fastest keydown performance
26036         if(Roo.isIE){
26037             return function(e){
26038                 var k = e.getKey(), r;
26039                 if(k == e.TAB){
26040                     e.stopEvent();
26041                     r = this.doc.selection.createRange();
26042                     if(r){
26043                         r.collapse(true);
26044                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26045                         this.deferFocus();
26046                     }
26047                     return;
26048                 }
26049                 
26050                 if(k == e.ENTER){
26051                     r = this.doc.selection.createRange();
26052                     if(r){
26053                         var target = r.parentElement();
26054                         if(!target || target.tagName.toLowerCase() != 'li'){
26055                             e.stopEvent();
26056                             r.pasteHTML('<br />');
26057                             r.collapse(false);
26058                             r.select();
26059                         }
26060                     }
26061                 }
26062                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26063                     this.cleanUpPaste.defer(100, this);
26064                     return;
26065                 }
26066                 
26067                 
26068             };
26069         }else if(Roo.isOpera){
26070             return function(e){
26071                 var k = e.getKey();
26072                 if(k == e.TAB){
26073                     e.stopEvent();
26074                     this.win.focus();
26075                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26076                     this.deferFocus();
26077                 }
26078                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26079                     this.cleanUpPaste.defer(100, this);
26080                     return;
26081                 }
26082                 
26083             };
26084         }else if(Roo.isSafari){
26085             return function(e){
26086                 var k = e.getKey();
26087                 
26088                 if(k == e.TAB){
26089                     e.stopEvent();
26090                     this.execCmd('InsertText','\t');
26091                     this.deferFocus();
26092                     return;
26093                 }
26094                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26095                     this.cleanUpPaste.defer(100, this);
26096                     return;
26097                 }
26098                 
26099              };
26100         }
26101     }(),
26102     
26103     getAllAncestors: function()
26104     {
26105         var p = this.getSelectedNode();
26106         var a = [];
26107         if (!p) {
26108             a.push(p); // push blank onto stack..
26109             p = this.getParentElement();
26110         }
26111         
26112         
26113         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26114             a.push(p);
26115             p = p.parentNode;
26116         }
26117         a.push(this.doc.body);
26118         return a;
26119     },
26120     lastSel : false,
26121     lastSelNode : false,
26122     
26123     
26124     getSelection : function() 
26125     {
26126         this.assignDocWin();
26127         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26128     },
26129     
26130     getSelectedNode: function() 
26131     {
26132         // this may only work on Gecko!!!
26133         
26134         // should we cache this!!!!
26135         
26136         
26137         
26138          
26139         var range = this.createRange(this.getSelection()).cloneRange();
26140         
26141         if (Roo.isIE) {
26142             var parent = range.parentElement();
26143             while (true) {
26144                 var testRange = range.duplicate();
26145                 testRange.moveToElementText(parent);
26146                 if (testRange.inRange(range)) {
26147                     break;
26148                 }
26149                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26150                     break;
26151                 }
26152                 parent = parent.parentElement;
26153             }
26154             return parent;
26155         }
26156         
26157         // is ancestor a text element.
26158         var ac =  range.commonAncestorContainer;
26159         if (ac.nodeType == 3) {
26160             ac = ac.parentNode;
26161         }
26162         
26163         var ar = ac.childNodes;
26164          
26165         var nodes = [];
26166         var other_nodes = [];
26167         var has_other_nodes = false;
26168         for (var i=0;i<ar.length;i++) {
26169             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26170                 continue;
26171             }
26172             // fullly contained node.
26173             
26174             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26175                 nodes.push(ar[i]);
26176                 continue;
26177             }
26178             
26179             // probably selected..
26180             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26181                 other_nodes.push(ar[i]);
26182                 continue;
26183             }
26184             // outer..
26185             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26186                 continue;
26187             }
26188             
26189             
26190             has_other_nodes = true;
26191         }
26192         if (!nodes.length && other_nodes.length) {
26193             nodes= other_nodes;
26194         }
26195         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26196             return false;
26197         }
26198         
26199         return nodes[0];
26200     },
26201     createRange: function(sel)
26202     {
26203         // this has strange effects when using with 
26204         // top toolbar - not sure if it's a great idea.
26205         //this.editor.contentWindow.focus();
26206         if (typeof sel != "undefined") {
26207             try {
26208                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26209             } catch(e) {
26210                 return this.doc.createRange();
26211             }
26212         } else {
26213             return this.doc.createRange();
26214         }
26215     },
26216     getParentElement: function()
26217     {
26218         
26219         this.assignDocWin();
26220         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26221         
26222         var range = this.createRange(sel);
26223          
26224         try {
26225             var p = range.commonAncestorContainer;
26226             while (p.nodeType == 3) { // text node
26227                 p = p.parentNode;
26228             }
26229             return p;
26230         } catch (e) {
26231             return null;
26232         }
26233     
26234     },
26235     /***
26236      *
26237      * Range intersection.. the hard stuff...
26238      *  '-1' = before
26239      *  '0' = hits..
26240      *  '1' = after.
26241      *         [ -- selected range --- ]
26242      *   [fail]                        [fail]
26243      *
26244      *    basically..
26245      *      if end is before start or  hits it. fail.
26246      *      if start is after end or hits it fail.
26247      *
26248      *   if either hits (but other is outside. - then it's not 
26249      *   
26250      *    
26251      **/
26252     
26253     
26254     // @see http://www.thismuchiknow.co.uk/?p=64.
26255     rangeIntersectsNode : function(range, node)
26256     {
26257         var nodeRange = node.ownerDocument.createRange();
26258         try {
26259             nodeRange.selectNode(node);
26260         } catch (e) {
26261             nodeRange.selectNodeContents(node);
26262         }
26263     
26264         var rangeStartRange = range.cloneRange();
26265         rangeStartRange.collapse(true);
26266     
26267         var rangeEndRange = range.cloneRange();
26268         rangeEndRange.collapse(false);
26269     
26270         var nodeStartRange = nodeRange.cloneRange();
26271         nodeStartRange.collapse(true);
26272     
26273         var nodeEndRange = nodeRange.cloneRange();
26274         nodeEndRange.collapse(false);
26275     
26276         return rangeStartRange.compareBoundaryPoints(
26277                  Range.START_TO_START, nodeEndRange) == -1 &&
26278                rangeEndRange.compareBoundaryPoints(
26279                  Range.START_TO_START, nodeStartRange) == 1;
26280         
26281          
26282     },
26283     rangeCompareNode : function(range, node)
26284     {
26285         var nodeRange = node.ownerDocument.createRange();
26286         try {
26287             nodeRange.selectNode(node);
26288         } catch (e) {
26289             nodeRange.selectNodeContents(node);
26290         }
26291         
26292         
26293         range.collapse(true);
26294     
26295         nodeRange.collapse(true);
26296      
26297         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26298         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26299          
26300         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26301         
26302         var nodeIsBefore   =  ss == 1;
26303         var nodeIsAfter    = ee == -1;
26304         
26305         if (nodeIsBefore && nodeIsAfter) {
26306             return 0; // outer
26307         }
26308         if (!nodeIsBefore && nodeIsAfter) {
26309             return 1; //right trailed.
26310         }
26311         
26312         if (nodeIsBefore && !nodeIsAfter) {
26313             return 2;  // left trailed.
26314         }
26315         // fully contined.
26316         return 3;
26317     },
26318
26319     // private? - in a new class?
26320     cleanUpPaste :  function()
26321     {
26322         // cleans up the whole document..
26323         Roo.log('cleanuppaste');
26324         
26325         this.cleanUpChildren(this.doc.body);
26326         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26327         if (clean != this.doc.body.innerHTML) {
26328             this.doc.body.innerHTML = clean;
26329         }
26330         
26331     },
26332     
26333     cleanWordChars : function(input) {// change the chars to hex code
26334         var he = Roo.HtmlEditorCore;
26335         
26336         var output = input;
26337         Roo.each(he.swapCodes, function(sw) { 
26338             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26339             
26340             output = output.replace(swapper, sw[1]);
26341         });
26342         
26343         return output;
26344     },
26345     
26346     
26347     cleanUpChildren : function (n)
26348     {
26349         if (!n.childNodes.length) {
26350             return;
26351         }
26352         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26353            this.cleanUpChild(n.childNodes[i]);
26354         }
26355     },
26356     
26357     
26358         
26359     
26360     cleanUpChild : function (node)
26361     {
26362         var ed = this;
26363         //console.log(node);
26364         if (node.nodeName == "#text") {
26365             // clean up silly Windows -- stuff?
26366             return; 
26367         }
26368         if (node.nodeName == "#comment") {
26369             node.parentNode.removeChild(node);
26370             // clean up silly Windows -- stuff?
26371             return; 
26372         }
26373         var lcname = node.tagName.toLowerCase();
26374         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26375         // whitelist of tags..
26376         
26377         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26378             // remove node.
26379             node.parentNode.removeChild(node);
26380             return;
26381             
26382         }
26383         
26384         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26385         
26386         // spans with no attributes - just remove them..
26387         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26388             remove_keep_children = true;
26389         }
26390         
26391         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26392         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26393         
26394         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26395         //    remove_keep_children = true;
26396         //}
26397         
26398         if (remove_keep_children) {
26399             this.cleanUpChildren(node);
26400             // inserts everything just before this node...
26401             while (node.childNodes.length) {
26402                 var cn = node.childNodes[0];
26403                 node.removeChild(cn);
26404                 node.parentNode.insertBefore(cn, node);
26405             }
26406             node.parentNode.removeChild(node);
26407             return;
26408         }
26409         
26410         if (!node.attributes || !node.attributes.length) {
26411             
26412           
26413             
26414             
26415             this.cleanUpChildren(node);
26416             return;
26417         }
26418         
26419         function cleanAttr(n,v)
26420         {
26421             
26422             if (v.match(/^\./) || v.match(/^\//)) {
26423                 return;
26424             }
26425             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26426                 return;
26427             }
26428             if (v.match(/^#/)) {
26429                 return;
26430             }
26431             if (v.match(/^\{/)) { // allow template editing.
26432                 return;
26433             }
26434 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26435             node.removeAttribute(n);
26436             
26437         }
26438         
26439         var cwhite = this.cwhite;
26440         var cblack = this.cblack;
26441             
26442         function cleanStyle(n,v)
26443         {
26444             if (v.match(/expression/)) { //XSS?? should we even bother..
26445                 node.removeAttribute(n);
26446                 return;
26447             }
26448             
26449             var parts = v.split(/;/);
26450             var clean = [];
26451             
26452             Roo.each(parts, function(p) {
26453                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26454                 if (!p.length) {
26455                     return true;
26456                 }
26457                 var l = p.split(':').shift().replace(/\s+/g,'');
26458                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26459                 
26460                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26461 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26462                     //node.removeAttribute(n);
26463                     return true;
26464                 }
26465                 //Roo.log()
26466                 // only allow 'c whitelisted system attributes'
26467                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26468 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26469                     //node.removeAttribute(n);
26470                     return true;
26471                 }
26472                 
26473                 
26474                  
26475                 
26476                 clean.push(p);
26477                 return true;
26478             });
26479             if (clean.length) { 
26480                 node.setAttribute(n, clean.join(';'));
26481             } else {
26482                 node.removeAttribute(n);
26483             }
26484             
26485         }
26486         
26487         
26488         for (var i = node.attributes.length-1; i > -1 ; i--) {
26489             var a = node.attributes[i];
26490             //console.log(a);
26491             
26492             if (a.name.toLowerCase().substr(0,2)=='on')  {
26493                 node.removeAttribute(a.name);
26494                 continue;
26495             }
26496             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26497                 node.removeAttribute(a.name);
26498                 continue;
26499             }
26500             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26501                 cleanAttr(a.name,a.value); // fixme..
26502                 continue;
26503             }
26504             if (a.name == 'style') {
26505                 cleanStyle(a.name,a.value);
26506                 continue;
26507             }
26508             /// clean up MS crap..
26509             // tecnically this should be a list of valid class'es..
26510             
26511             
26512             if (a.name == 'class') {
26513                 if (a.value.match(/^Mso/)) {
26514                     node.removeAttribute('class');
26515                 }
26516                 
26517                 if (a.value.match(/^body$/)) {
26518                     node.removeAttribute('class');
26519                 }
26520                 continue;
26521             }
26522             
26523             // style cleanup!?
26524             // class cleanup?
26525             
26526         }
26527         
26528         
26529         this.cleanUpChildren(node);
26530         
26531         
26532     },
26533     
26534     /**
26535      * Clean up MS wordisms...
26536      */
26537     cleanWord : function(node)
26538     {
26539         if (!node) {
26540             this.cleanWord(this.doc.body);
26541             return;
26542         }
26543         
26544         if(
26545                 node.nodeName == 'SPAN' &&
26546                 !node.hasAttributes() &&
26547                 node.childNodes.length == 1 &&
26548                 node.firstChild.nodeName == "#text"  
26549         ) {
26550             var textNode = node.firstChild;
26551             node.removeChild(textNode);
26552             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26553                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26554             }
26555             node.parentNode.insertBefore(textNode, node);
26556             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26557                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26558             }
26559             node.parentNode.removeChild(node);
26560         }
26561         
26562         if (node.nodeName == "#text") {
26563             // clean up silly Windows -- stuff?
26564             return; 
26565         }
26566         if (node.nodeName == "#comment") {
26567             node.parentNode.removeChild(node);
26568             // clean up silly Windows -- stuff?
26569             return; 
26570         }
26571         
26572         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26573             node.parentNode.removeChild(node);
26574             return;
26575         }
26576         //Roo.log(node.tagName);
26577         // remove - but keep children..
26578         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26579             //Roo.log('-- removed');
26580             while (node.childNodes.length) {
26581                 var cn = node.childNodes[0];
26582                 node.removeChild(cn);
26583                 node.parentNode.insertBefore(cn, node);
26584                 // move node to parent - and clean it..
26585                 this.cleanWord(cn);
26586             }
26587             node.parentNode.removeChild(node);
26588             /// no need to iterate chidlren = it's got none..
26589             //this.iterateChildren(node, this.cleanWord);
26590             return;
26591         }
26592         // clean styles
26593         if (node.className.length) {
26594             
26595             var cn = node.className.split(/\W+/);
26596             var cna = [];
26597             Roo.each(cn, function(cls) {
26598                 if (cls.match(/Mso[a-zA-Z]+/)) {
26599                     return;
26600                 }
26601                 cna.push(cls);
26602             });
26603             node.className = cna.length ? cna.join(' ') : '';
26604             if (!cna.length) {
26605                 node.removeAttribute("class");
26606             }
26607         }
26608         
26609         if (node.hasAttribute("lang")) {
26610             node.removeAttribute("lang");
26611         }
26612         
26613         if (node.hasAttribute("style")) {
26614             
26615             var styles = node.getAttribute("style").split(";");
26616             var nstyle = [];
26617             Roo.each(styles, function(s) {
26618                 if (!s.match(/:/)) {
26619                     return;
26620                 }
26621                 var kv = s.split(":");
26622                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26623                     return;
26624                 }
26625                 // what ever is left... we allow.
26626                 nstyle.push(s);
26627             });
26628             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26629             if (!nstyle.length) {
26630                 node.removeAttribute('style');
26631             }
26632         }
26633         this.iterateChildren(node, this.cleanWord);
26634         
26635         
26636         
26637     },
26638     /**
26639      * iterateChildren of a Node, calling fn each time, using this as the scole..
26640      * @param {DomNode} node node to iterate children of.
26641      * @param {Function} fn method of this class to call on each item.
26642      */
26643     iterateChildren : function(node, fn)
26644     {
26645         if (!node.childNodes.length) {
26646                 return;
26647         }
26648         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26649            fn.call(this, node.childNodes[i])
26650         }
26651     },
26652     
26653     
26654     /**
26655      * cleanTableWidths.
26656      *
26657      * Quite often pasting from word etc.. results in tables with column and widths.
26658      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26659      *
26660      */
26661     cleanTableWidths : function(node)
26662     {
26663          
26664          
26665         if (!node) {
26666             this.cleanTableWidths(this.doc.body);
26667             return;
26668         }
26669         
26670         // ignore list...
26671         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26672             return; 
26673         }
26674         Roo.log(node.tagName);
26675         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26676             this.iterateChildren(node, this.cleanTableWidths);
26677             return;
26678         }
26679         if (node.hasAttribute('width')) {
26680             node.removeAttribute('width');
26681         }
26682         
26683          
26684         if (node.hasAttribute("style")) {
26685             // pretty basic...
26686             
26687             var styles = node.getAttribute("style").split(";");
26688             var nstyle = [];
26689             Roo.each(styles, function(s) {
26690                 if (!s.match(/:/)) {
26691                     return;
26692                 }
26693                 var kv = s.split(":");
26694                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26695                     return;
26696                 }
26697                 // what ever is left... we allow.
26698                 nstyle.push(s);
26699             });
26700             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26701             if (!nstyle.length) {
26702                 node.removeAttribute('style');
26703             }
26704         }
26705         
26706         this.iterateChildren(node, this.cleanTableWidths);
26707         
26708         
26709     },
26710     
26711     
26712     
26713     
26714     domToHTML : function(currentElement, depth, nopadtext) {
26715         
26716         depth = depth || 0;
26717         nopadtext = nopadtext || false;
26718     
26719         if (!currentElement) {
26720             return this.domToHTML(this.doc.body);
26721         }
26722         
26723         //Roo.log(currentElement);
26724         var j;
26725         var allText = false;
26726         var nodeName = currentElement.nodeName;
26727         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26728         
26729         if  (nodeName == '#text') {
26730             
26731             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26732         }
26733         
26734         
26735         var ret = '';
26736         if (nodeName != 'BODY') {
26737              
26738             var i = 0;
26739             // Prints the node tagName, such as <A>, <IMG>, etc
26740             if (tagName) {
26741                 var attr = [];
26742                 for(i = 0; i < currentElement.attributes.length;i++) {
26743                     // quoting?
26744                     var aname = currentElement.attributes.item(i).name;
26745                     if (!currentElement.attributes.item(i).value.length) {
26746                         continue;
26747                     }
26748                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26749                 }
26750                 
26751                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26752             } 
26753             else {
26754                 
26755                 // eack
26756             }
26757         } else {
26758             tagName = false;
26759         }
26760         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26761             return ret;
26762         }
26763         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26764             nopadtext = true;
26765         }
26766         
26767         
26768         // Traverse the tree
26769         i = 0;
26770         var currentElementChild = currentElement.childNodes.item(i);
26771         var allText = true;
26772         var innerHTML  = '';
26773         lastnode = '';
26774         while (currentElementChild) {
26775             // Formatting code (indent the tree so it looks nice on the screen)
26776             var nopad = nopadtext;
26777             if (lastnode == 'SPAN') {
26778                 nopad  = true;
26779             }
26780             // text
26781             if  (currentElementChild.nodeName == '#text') {
26782                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26783                 toadd = nopadtext ? toadd : toadd.trim();
26784                 if (!nopad && toadd.length > 80) {
26785                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26786                 }
26787                 innerHTML  += toadd;
26788                 
26789                 i++;
26790                 currentElementChild = currentElement.childNodes.item(i);
26791                 lastNode = '';
26792                 continue;
26793             }
26794             allText = false;
26795             
26796             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26797                 
26798             // Recursively traverse the tree structure of the child node
26799             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26800             lastnode = currentElementChild.nodeName;
26801             i++;
26802             currentElementChild=currentElement.childNodes.item(i);
26803         }
26804         
26805         ret += innerHTML;
26806         
26807         if (!allText) {
26808                 // The remaining code is mostly for formatting the tree
26809             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26810         }
26811         
26812         
26813         if (tagName) {
26814             ret+= "</"+tagName+">";
26815         }
26816         return ret;
26817         
26818     },
26819         
26820     applyBlacklists : function()
26821     {
26822         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26823         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26824         
26825         this.white = [];
26826         this.black = [];
26827         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26828             if (b.indexOf(tag) > -1) {
26829                 return;
26830             }
26831             this.white.push(tag);
26832             
26833         }, this);
26834         
26835         Roo.each(w, function(tag) {
26836             if (b.indexOf(tag) > -1) {
26837                 return;
26838             }
26839             if (this.white.indexOf(tag) > -1) {
26840                 return;
26841             }
26842             this.white.push(tag);
26843             
26844         }, this);
26845         
26846         
26847         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26848             if (w.indexOf(tag) > -1) {
26849                 return;
26850             }
26851             this.black.push(tag);
26852             
26853         }, this);
26854         
26855         Roo.each(b, function(tag) {
26856             if (w.indexOf(tag) > -1) {
26857                 return;
26858             }
26859             if (this.black.indexOf(tag) > -1) {
26860                 return;
26861             }
26862             this.black.push(tag);
26863             
26864         }, this);
26865         
26866         
26867         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26868         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26869         
26870         this.cwhite = [];
26871         this.cblack = [];
26872         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26873             if (b.indexOf(tag) > -1) {
26874                 return;
26875             }
26876             this.cwhite.push(tag);
26877             
26878         }, this);
26879         
26880         Roo.each(w, function(tag) {
26881             if (b.indexOf(tag) > -1) {
26882                 return;
26883             }
26884             if (this.cwhite.indexOf(tag) > -1) {
26885                 return;
26886             }
26887             this.cwhite.push(tag);
26888             
26889         }, this);
26890         
26891         
26892         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26893             if (w.indexOf(tag) > -1) {
26894                 return;
26895             }
26896             this.cblack.push(tag);
26897             
26898         }, this);
26899         
26900         Roo.each(b, function(tag) {
26901             if (w.indexOf(tag) > -1) {
26902                 return;
26903             }
26904             if (this.cblack.indexOf(tag) > -1) {
26905                 return;
26906             }
26907             this.cblack.push(tag);
26908             
26909         }, this);
26910     },
26911     
26912     setStylesheets : function(stylesheets)
26913     {
26914         if(typeof(stylesheets) == 'string'){
26915             Roo.get(this.iframe.contentDocument.head).createChild({
26916                 tag : 'link',
26917                 rel : 'stylesheet',
26918                 type : 'text/css',
26919                 href : stylesheets
26920             });
26921             
26922             return;
26923         }
26924         var _this = this;
26925      
26926         Roo.each(stylesheets, function(s) {
26927             if(!s.length){
26928                 return;
26929             }
26930             
26931             Roo.get(_this.iframe.contentDocument.head).createChild({
26932                 tag : 'link',
26933                 rel : 'stylesheet',
26934                 type : 'text/css',
26935                 href : s
26936             });
26937         });
26938
26939         
26940     },
26941     
26942     removeStylesheets : function()
26943     {
26944         var _this = this;
26945         
26946         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26947             s.remove();
26948         });
26949     },
26950     
26951     setStyle : function(style)
26952     {
26953         Roo.get(this.iframe.contentDocument.head).createChild({
26954             tag : 'style',
26955             type : 'text/css',
26956             html : style
26957         });
26958
26959         return;
26960     }
26961     
26962     // hide stuff that is not compatible
26963     /**
26964      * @event blur
26965      * @hide
26966      */
26967     /**
26968      * @event change
26969      * @hide
26970      */
26971     /**
26972      * @event focus
26973      * @hide
26974      */
26975     /**
26976      * @event specialkey
26977      * @hide
26978      */
26979     /**
26980      * @cfg {String} fieldClass @hide
26981      */
26982     /**
26983      * @cfg {String} focusClass @hide
26984      */
26985     /**
26986      * @cfg {String} autoCreate @hide
26987      */
26988     /**
26989      * @cfg {String} inputType @hide
26990      */
26991     /**
26992      * @cfg {String} invalidClass @hide
26993      */
26994     /**
26995      * @cfg {String} invalidText @hide
26996      */
26997     /**
26998      * @cfg {String} msgFx @hide
26999      */
27000     /**
27001      * @cfg {String} validateOnBlur @hide
27002      */
27003 });
27004
27005 Roo.HtmlEditorCore.white = [
27006         'area', 'br', 'img', 'input', 'hr', 'wbr',
27007         
27008        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27009        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27010        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27011        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27012        'table',   'ul',         'xmp', 
27013        
27014        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27015       'thead',   'tr', 
27016      
27017       'dir', 'menu', 'ol', 'ul', 'dl',
27018        
27019       'embed',  'object'
27020 ];
27021
27022
27023 Roo.HtmlEditorCore.black = [
27024     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27025         'applet', // 
27026         'base',   'basefont', 'bgsound', 'blink',  'body', 
27027         'frame',  'frameset', 'head',    'html',   'ilayer', 
27028         'iframe', 'layer',  'link',     'meta',    'object',   
27029         'script', 'style' ,'title',  'xml' // clean later..
27030 ];
27031 Roo.HtmlEditorCore.clean = [
27032     'script', 'style', 'title', 'xml'
27033 ];
27034 Roo.HtmlEditorCore.remove = [
27035     'font'
27036 ];
27037 // attributes..
27038
27039 Roo.HtmlEditorCore.ablack = [
27040     'on'
27041 ];
27042     
27043 Roo.HtmlEditorCore.aclean = [ 
27044     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27045 ];
27046
27047 // protocols..
27048 Roo.HtmlEditorCore.pwhite= [
27049         'http',  'https',  'mailto'
27050 ];
27051
27052 // white listed style attributes.
27053 Roo.HtmlEditorCore.cwhite= [
27054       //  'text-align', /// default is to allow most things..
27055       
27056          
27057 //        'font-size'//??
27058 ];
27059
27060 // black listed style attributes.
27061 Roo.HtmlEditorCore.cblack= [
27062       //  'font-size' -- this can be set by the project 
27063 ];
27064
27065
27066 Roo.HtmlEditorCore.swapCodes   =[ 
27067     [    8211, "&#8211;" ], 
27068     [    8212, "&#8212;" ], 
27069     [    8216,  "'" ],  
27070     [    8217, "'" ],  
27071     [    8220, '"' ],  
27072     [    8221, '"' ],  
27073     [    8226, "*" ],  
27074     [    8230, "..." ]
27075 ]; 
27076
27077     /*
27078  * - LGPL
27079  *
27080  * HtmlEditor
27081  * 
27082  */
27083
27084 /**
27085  * @class Roo.bootstrap.HtmlEditor
27086  * @extends Roo.bootstrap.TextArea
27087  * Bootstrap HtmlEditor class
27088
27089  * @constructor
27090  * Create a new HtmlEditor
27091  * @param {Object} config The config object
27092  */
27093
27094 Roo.bootstrap.HtmlEditor = function(config){
27095     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27096     if (!this.toolbars) {
27097         this.toolbars = [];
27098     }
27099     
27100     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27101     this.addEvents({
27102             /**
27103              * @event initialize
27104              * Fires when the editor is fully initialized (including the iframe)
27105              * @param {HtmlEditor} this
27106              */
27107             initialize: true,
27108             /**
27109              * @event activate
27110              * Fires when the editor is first receives the focus. Any insertion must wait
27111              * until after this event.
27112              * @param {HtmlEditor} this
27113              */
27114             activate: true,
27115              /**
27116              * @event beforesync
27117              * Fires before the textarea is updated with content from the editor iframe. Return false
27118              * to cancel the sync.
27119              * @param {HtmlEditor} this
27120              * @param {String} html
27121              */
27122             beforesync: true,
27123              /**
27124              * @event beforepush
27125              * Fires before the iframe editor is updated with content from the textarea. Return false
27126              * to cancel the push.
27127              * @param {HtmlEditor} this
27128              * @param {String} html
27129              */
27130             beforepush: true,
27131              /**
27132              * @event sync
27133              * Fires when the textarea is updated with content from the editor iframe.
27134              * @param {HtmlEditor} this
27135              * @param {String} html
27136              */
27137             sync: true,
27138              /**
27139              * @event push
27140              * Fires when the iframe editor is updated with content from the textarea.
27141              * @param {HtmlEditor} this
27142              * @param {String} html
27143              */
27144             push: true,
27145              /**
27146              * @event editmodechange
27147              * Fires when the editor switches edit modes
27148              * @param {HtmlEditor} this
27149              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27150              */
27151             editmodechange: true,
27152             /**
27153              * @event editorevent
27154              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27155              * @param {HtmlEditor} this
27156              */
27157             editorevent: true,
27158             /**
27159              * @event firstfocus
27160              * Fires when on first focus - needed by toolbars..
27161              * @param {HtmlEditor} this
27162              */
27163             firstfocus: true,
27164             /**
27165              * @event autosave
27166              * Auto save the htmlEditor value as a file into Events
27167              * @param {HtmlEditor} this
27168              */
27169             autosave: true,
27170             /**
27171              * @event savedpreview
27172              * preview the saved version of htmlEditor
27173              * @param {HtmlEditor} this
27174              */
27175             savedpreview: true
27176         });
27177 };
27178
27179
27180 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27181     
27182     
27183       /**
27184      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27185      */
27186     toolbars : false,
27187     
27188      /**
27189     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27190     */
27191     btns : [],
27192    
27193      /**
27194      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27195      *                        Roo.resizable.
27196      */
27197     resizable : false,
27198      /**
27199      * @cfg {Number} height (in pixels)
27200      */   
27201     height: 300,
27202    /**
27203      * @cfg {Number} width (in pixels)
27204      */   
27205     width: false,
27206     
27207     /**
27208      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27209      * 
27210      */
27211     stylesheets: false,
27212     
27213     // id of frame..
27214     frameId: false,
27215     
27216     // private properties
27217     validationEvent : false,
27218     deferHeight: true,
27219     initialized : false,
27220     activated : false,
27221     
27222     onFocus : Roo.emptyFn,
27223     iframePad:3,
27224     hideMode:'offsets',
27225     
27226     tbContainer : false,
27227     
27228     bodyCls : '',
27229     
27230     toolbarContainer :function() {
27231         return this.wrap.select('.x-html-editor-tb',true).first();
27232     },
27233
27234     /**
27235      * Protected method that will not generally be called directly. It
27236      * is called when the editor creates its toolbar. Override this method if you need to
27237      * add custom toolbar buttons.
27238      * @param {HtmlEditor} editor
27239      */
27240     createToolbar : function(){
27241         Roo.log('renewing');
27242         Roo.log("create toolbars");
27243         
27244         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27245         this.toolbars[0].render(this.toolbarContainer());
27246         
27247         return;
27248         
27249 //        if (!editor.toolbars || !editor.toolbars.length) {
27250 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27251 //        }
27252 //        
27253 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27254 //            editor.toolbars[i] = Roo.factory(
27255 //                    typeof(editor.toolbars[i]) == 'string' ?
27256 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27257 //                Roo.bootstrap.HtmlEditor);
27258 //            editor.toolbars[i].init(editor);
27259 //        }
27260     },
27261
27262      
27263     // private
27264     onRender : function(ct, position)
27265     {
27266        // Roo.log("Call onRender: " + this.xtype);
27267         var _t = this;
27268         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27269       
27270         this.wrap = this.inputEl().wrap({
27271             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27272         });
27273         
27274         this.editorcore.onRender(ct, position);
27275          
27276         if (this.resizable) {
27277             this.resizeEl = new Roo.Resizable(this.wrap, {
27278                 pinned : true,
27279                 wrap: true,
27280                 dynamic : true,
27281                 minHeight : this.height,
27282                 height: this.height,
27283                 handles : this.resizable,
27284                 width: this.width,
27285                 listeners : {
27286                     resize : function(r, w, h) {
27287                         _t.onResize(w,h); // -something
27288                     }
27289                 }
27290             });
27291             
27292         }
27293         this.createToolbar(this);
27294        
27295         
27296         if(!this.width && this.resizable){
27297             this.setSize(this.wrap.getSize());
27298         }
27299         if (this.resizeEl) {
27300             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27301             // should trigger onReize..
27302         }
27303         
27304     },
27305
27306     // private
27307     onResize : function(w, h)
27308     {
27309         Roo.log('resize: ' +w + ',' + h );
27310         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27311         var ew = false;
27312         var eh = false;
27313         
27314         if(this.inputEl() ){
27315             if(typeof w == 'number'){
27316                 var aw = w - this.wrap.getFrameWidth('lr');
27317                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27318                 ew = aw;
27319             }
27320             if(typeof h == 'number'){
27321                  var tbh = -11;  // fixme it needs to tool bar size!
27322                 for (var i =0; i < this.toolbars.length;i++) {
27323                     // fixme - ask toolbars for heights?
27324                     tbh += this.toolbars[i].el.getHeight();
27325                     //if (this.toolbars[i].footer) {
27326                     //    tbh += this.toolbars[i].footer.el.getHeight();
27327                     //}
27328                 }
27329               
27330                 
27331                 
27332                 
27333                 
27334                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27335                 ah -= 5; // knock a few pixes off for look..
27336                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27337                 var eh = ah;
27338             }
27339         }
27340         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27341         this.editorcore.onResize(ew,eh);
27342         
27343     },
27344
27345     /**
27346      * Toggles the editor between standard and source edit mode.
27347      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27348      */
27349     toggleSourceEdit : function(sourceEditMode)
27350     {
27351         this.editorcore.toggleSourceEdit(sourceEditMode);
27352         
27353         if(this.editorcore.sourceEditMode){
27354             Roo.log('editor - showing textarea');
27355             
27356 //            Roo.log('in');
27357 //            Roo.log(this.syncValue());
27358             this.syncValue();
27359             this.inputEl().removeClass(['hide', 'x-hidden']);
27360             this.inputEl().dom.removeAttribute('tabIndex');
27361             this.inputEl().focus();
27362         }else{
27363             Roo.log('editor - hiding textarea');
27364 //            Roo.log('out')
27365 //            Roo.log(this.pushValue()); 
27366             this.pushValue();
27367             
27368             this.inputEl().addClass(['hide', 'x-hidden']);
27369             this.inputEl().dom.setAttribute('tabIndex', -1);
27370             //this.deferFocus();
27371         }
27372          
27373         if(this.resizable){
27374             this.setSize(this.wrap.getSize());
27375         }
27376         
27377         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27378     },
27379  
27380     // private (for BoxComponent)
27381     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27382
27383     // private (for BoxComponent)
27384     getResizeEl : function(){
27385         return this.wrap;
27386     },
27387
27388     // private (for BoxComponent)
27389     getPositionEl : function(){
27390         return this.wrap;
27391     },
27392
27393     // private
27394     initEvents : function(){
27395         this.originalValue = this.getValue();
27396     },
27397
27398 //    /**
27399 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27400 //     * @method
27401 //     */
27402 //    markInvalid : Roo.emptyFn,
27403 //    /**
27404 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27405 //     * @method
27406 //     */
27407 //    clearInvalid : Roo.emptyFn,
27408
27409     setValue : function(v){
27410         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27411         this.editorcore.pushValue();
27412     },
27413
27414      
27415     // private
27416     deferFocus : function(){
27417         this.focus.defer(10, this);
27418     },
27419
27420     // doc'ed in Field
27421     focus : function(){
27422         this.editorcore.focus();
27423         
27424     },
27425       
27426
27427     // private
27428     onDestroy : function(){
27429         
27430         
27431         
27432         if(this.rendered){
27433             
27434             for (var i =0; i < this.toolbars.length;i++) {
27435                 // fixme - ask toolbars for heights?
27436                 this.toolbars[i].onDestroy();
27437             }
27438             
27439             this.wrap.dom.innerHTML = '';
27440             this.wrap.remove();
27441         }
27442     },
27443
27444     // private
27445     onFirstFocus : function(){
27446         //Roo.log("onFirstFocus");
27447         this.editorcore.onFirstFocus();
27448          for (var i =0; i < this.toolbars.length;i++) {
27449             this.toolbars[i].onFirstFocus();
27450         }
27451         
27452     },
27453     
27454     // private
27455     syncValue : function()
27456     {   
27457         this.editorcore.syncValue();
27458     },
27459     
27460     pushValue : function()
27461     {   
27462         this.editorcore.pushValue();
27463     }
27464      
27465     
27466     // hide stuff that is not compatible
27467     /**
27468      * @event blur
27469      * @hide
27470      */
27471     /**
27472      * @event change
27473      * @hide
27474      */
27475     /**
27476      * @event focus
27477      * @hide
27478      */
27479     /**
27480      * @event specialkey
27481      * @hide
27482      */
27483     /**
27484      * @cfg {String} fieldClass @hide
27485      */
27486     /**
27487      * @cfg {String} focusClass @hide
27488      */
27489     /**
27490      * @cfg {String} autoCreate @hide
27491      */
27492     /**
27493      * @cfg {String} inputType @hide
27494      */
27495      
27496     /**
27497      * @cfg {String} invalidText @hide
27498      */
27499     /**
27500      * @cfg {String} msgFx @hide
27501      */
27502     /**
27503      * @cfg {String} validateOnBlur @hide
27504      */
27505 });
27506  
27507     
27508    
27509    
27510    
27511       
27512 Roo.namespace('Roo.bootstrap.htmleditor');
27513 /**
27514  * @class Roo.bootstrap.HtmlEditorToolbar1
27515  * Basic Toolbar
27516  * 
27517  * @example
27518  * Usage:
27519  *
27520  new Roo.bootstrap.HtmlEditor({
27521     ....
27522     toolbars : [
27523         new Roo.bootstrap.HtmlEditorToolbar1({
27524             disable : { fonts: 1 , format: 1, ..., ... , ...],
27525             btns : [ .... ]
27526         })
27527     }
27528      
27529  * 
27530  * @cfg {Object} disable List of elements to disable..
27531  * @cfg {Array} btns List of additional buttons.
27532  * 
27533  * 
27534  * NEEDS Extra CSS? 
27535  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27536  */
27537  
27538 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27539 {
27540     
27541     Roo.apply(this, config);
27542     
27543     // default disabled, based on 'good practice'..
27544     this.disable = this.disable || {};
27545     Roo.applyIf(this.disable, {
27546         fontSize : true,
27547         colors : true,
27548         specialElements : true
27549     });
27550     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27551     
27552     this.editor = config.editor;
27553     this.editorcore = config.editor.editorcore;
27554     
27555     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27556     
27557     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27558     // dont call parent... till later.
27559 }
27560 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27561      
27562     bar : true,
27563     
27564     editor : false,
27565     editorcore : false,
27566     
27567     
27568     formats : [
27569         "p" ,  
27570         "h1","h2","h3","h4","h5","h6", 
27571         "pre", "code", 
27572         "abbr", "acronym", "address", "cite", "samp", "var",
27573         'div','span'
27574     ],
27575     
27576     onRender : function(ct, position)
27577     {
27578        // Roo.log("Call onRender: " + this.xtype);
27579         
27580        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27581        Roo.log(this.el);
27582        this.el.dom.style.marginBottom = '0';
27583        var _this = this;
27584        var editorcore = this.editorcore;
27585        var editor= this.editor;
27586        
27587        var children = [];
27588        var btn = function(id,cmd , toggle, handler, html){
27589        
27590             var  event = toggle ? 'toggle' : 'click';
27591        
27592             var a = {
27593                 size : 'sm',
27594                 xtype: 'Button',
27595                 xns: Roo.bootstrap,
27596                 //glyphicon : id,
27597                 fa: id,
27598                 cmd : id || cmd,
27599                 enableToggle:toggle !== false,
27600                 html : html || '',
27601                 pressed : toggle ? false : null,
27602                 listeners : {}
27603             };
27604             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27605                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27606             };
27607             children.push(a);
27608             return a;
27609        }
27610        
27611     //    var cb_box = function...
27612         
27613         var style = {
27614                 xtype: 'Button',
27615                 size : 'sm',
27616                 xns: Roo.bootstrap,
27617                 fa : 'font',
27618                 //html : 'submit'
27619                 menu : {
27620                     xtype: 'Menu',
27621                     xns: Roo.bootstrap,
27622                     items:  []
27623                 }
27624         };
27625         Roo.each(this.formats, function(f) {
27626             style.menu.items.push({
27627                 xtype :'MenuItem',
27628                 xns: Roo.bootstrap,
27629                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27630                 tagname : f,
27631                 listeners : {
27632                     click : function()
27633                     {
27634                         editorcore.insertTag(this.tagname);
27635                         editor.focus();
27636                     }
27637                 }
27638                 
27639             });
27640         });
27641         children.push(style);   
27642         
27643         btn('bold',false,true);
27644         btn('italic',false,true);
27645         btn('align-left', 'justifyleft',true);
27646         btn('align-center', 'justifycenter',true);
27647         btn('align-right' , 'justifyright',true);
27648         btn('link', false, false, function(btn) {
27649             //Roo.log("create link?");
27650             var url = prompt(this.createLinkText, this.defaultLinkValue);
27651             if(url && url != 'http:/'+'/'){
27652                 this.editorcore.relayCmd('createlink', url);
27653             }
27654         }),
27655         btn('list','insertunorderedlist',true);
27656         btn('pencil', false,true, function(btn){
27657                 Roo.log(this);
27658                 this.toggleSourceEdit(btn.pressed);
27659         });
27660         
27661         if (this.editor.btns.length > 0) {
27662             for (var i = 0; i<this.editor.btns.length; i++) {
27663                 children.push(this.editor.btns[i]);
27664             }
27665         }
27666         
27667         /*
27668         var cog = {
27669                 xtype: 'Button',
27670                 size : 'sm',
27671                 xns: Roo.bootstrap,
27672                 glyphicon : 'cog',
27673                 //html : 'submit'
27674                 menu : {
27675                     xtype: 'Menu',
27676                     xns: Roo.bootstrap,
27677                     items:  []
27678                 }
27679         };
27680         
27681         cog.menu.items.push({
27682             xtype :'MenuItem',
27683             xns: Roo.bootstrap,
27684             html : Clean styles,
27685             tagname : f,
27686             listeners : {
27687                 click : function()
27688                 {
27689                     editorcore.insertTag(this.tagname);
27690                     editor.focus();
27691                 }
27692             }
27693             
27694         });
27695        */
27696         
27697          
27698        this.xtype = 'NavSimplebar';
27699         
27700         for(var i=0;i< children.length;i++) {
27701             
27702             this.buttons.add(this.addxtypeChild(children[i]));
27703             
27704         }
27705         
27706         editor.on('editorevent', this.updateToolbar, this);
27707     },
27708     onBtnClick : function(id)
27709     {
27710        this.editorcore.relayCmd(id);
27711        this.editorcore.focus();
27712     },
27713     
27714     /**
27715      * Protected method that will not generally be called directly. It triggers
27716      * a toolbar update by reading the markup state of the current selection in the editor.
27717      */
27718     updateToolbar: function(){
27719
27720         if(!this.editorcore.activated){
27721             this.editor.onFirstFocus(); // is this neeed?
27722             return;
27723         }
27724
27725         var btns = this.buttons; 
27726         var doc = this.editorcore.doc;
27727         btns.get('bold').setActive(doc.queryCommandState('bold'));
27728         btns.get('italic').setActive(doc.queryCommandState('italic'));
27729         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27730         
27731         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27732         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27733         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27734         
27735         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27736         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27737          /*
27738         
27739         var ans = this.editorcore.getAllAncestors();
27740         if (this.formatCombo) {
27741             
27742             
27743             var store = this.formatCombo.store;
27744             this.formatCombo.setValue("");
27745             for (var i =0; i < ans.length;i++) {
27746                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27747                     // select it..
27748                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27749                     break;
27750                 }
27751             }
27752         }
27753         
27754         
27755         
27756         // hides menus... - so this cant be on a menu...
27757         Roo.bootstrap.MenuMgr.hideAll();
27758         */
27759         Roo.bootstrap.MenuMgr.hideAll();
27760         //this.editorsyncValue();
27761     },
27762     onFirstFocus: function() {
27763         this.buttons.each(function(item){
27764            item.enable();
27765         });
27766     },
27767     toggleSourceEdit : function(sourceEditMode){
27768         
27769           
27770         if(sourceEditMode){
27771             Roo.log("disabling buttons");
27772            this.buttons.each( function(item){
27773                 if(item.cmd != 'pencil'){
27774                     item.disable();
27775                 }
27776             });
27777           
27778         }else{
27779             Roo.log("enabling buttons");
27780             if(this.editorcore.initialized){
27781                 this.buttons.each( function(item){
27782                     item.enable();
27783                 });
27784             }
27785             
27786         }
27787         Roo.log("calling toggole on editor");
27788         // tell the editor that it's been pressed..
27789         this.editor.toggleSourceEdit(sourceEditMode);
27790        
27791     }
27792 });
27793
27794
27795
27796
27797  
27798 /*
27799  * - LGPL
27800  */
27801
27802 /**
27803  * @class Roo.bootstrap.Markdown
27804  * @extends Roo.bootstrap.TextArea
27805  * Bootstrap Showdown editable area
27806  * @cfg {string} content
27807  * 
27808  * @constructor
27809  * Create a new Showdown
27810  */
27811
27812 Roo.bootstrap.Markdown = function(config){
27813     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27814    
27815 };
27816
27817 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27818     
27819     editing :false,
27820     
27821     initEvents : function()
27822     {
27823         
27824         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27825         this.markdownEl = this.el.createChild({
27826             cls : 'roo-markdown-area'
27827         });
27828         this.inputEl().addClass('d-none');
27829         if (this.getValue() == '') {
27830             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27831             
27832         } else {
27833             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27834         }
27835         this.markdownEl.on('click', this.toggleTextEdit, this);
27836         this.on('blur', this.toggleTextEdit, this);
27837         this.on('specialkey', this.resizeTextArea, this);
27838     },
27839     
27840     toggleTextEdit : function()
27841     {
27842         var sh = this.markdownEl.getHeight();
27843         this.inputEl().addClass('d-none');
27844         this.markdownEl.addClass('d-none');
27845         if (!this.editing) {
27846             // show editor?
27847             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27848             this.inputEl().removeClass('d-none');
27849             this.inputEl().focus();
27850             this.editing = true;
27851             return;
27852         }
27853         // show showdown...
27854         this.updateMarkdown();
27855         this.markdownEl.removeClass('d-none');
27856         this.editing = false;
27857         return;
27858     },
27859     updateMarkdown : function()
27860     {
27861         if (this.getValue() == '') {
27862             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27863             return;
27864         }
27865  
27866         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27867     },
27868     
27869     resizeTextArea: function () {
27870         
27871         var sh = 100;
27872         Roo.log([sh, this.getValue().split("\n").length * 30]);
27873         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27874     },
27875     setValue : function(val)
27876     {
27877         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27878         if (!this.editing) {
27879             this.updateMarkdown();
27880         }
27881         
27882     },
27883     focus : function()
27884     {
27885         if (!this.editing) {
27886             this.toggleTextEdit();
27887         }
27888         
27889     }
27890
27891
27892 });/*
27893  * Based on:
27894  * Ext JS Library 1.1.1
27895  * Copyright(c) 2006-2007, Ext JS, LLC.
27896  *
27897  * Originally Released Under LGPL - original licence link has changed is not relivant.
27898  *
27899  * Fork - LGPL
27900  * <script type="text/javascript">
27901  */
27902  
27903 /**
27904  * @class Roo.bootstrap.PagingToolbar
27905  * @extends Roo.bootstrap.NavSimplebar
27906  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27907  * @constructor
27908  * Create a new PagingToolbar
27909  * @param {Object} config The config object
27910  * @param {Roo.data.Store} store
27911  */
27912 Roo.bootstrap.PagingToolbar = function(config)
27913 {
27914     // old args format still supported... - xtype is prefered..
27915         // created from xtype...
27916     
27917     this.ds = config.dataSource;
27918     
27919     if (config.store && !this.ds) {
27920         this.store= Roo.factory(config.store, Roo.data);
27921         this.ds = this.store;
27922         this.ds.xmodule = this.xmodule || false;
27923     }
27924     
27925     this.toolbarItems = [];
27926     if (config.items) {
27927         this.toolbarItems = config.items;
27928     }
27929     
27930     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27931     
27932     this.cursor = 0;
27933     
27934     if (this.ds) { 
27935         this.bind(this.ds);
27936     }
27937     
27938     if (Roo.bootstrap.version == 4) {
27939         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27940     } else {
27941         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27942     }
27943     
27944 };
27945
27946 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27947     /**
27948      * @cfg {Roo.data.Store} dataSource
27949      * The underlying data store providing the paged data
27950      */
27951     /**
27952      * @cfg {String/HTMLElement/Element} container
27953      * container The id or element that will contain the toolbar
27954      */
27955     /**
27956      * @cfg {Boolean} displayInfo
27957      * True to display the displayMsg (defaults to false)
27958      */
27959     /**
27960      * @cfg {Number} pageSize
27961      * The number of records to display per page (defaults to 20)
27962      */
27963     pageSize: 20,
27964     /**
27965      * @cfg {String} displayMsg
27966      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27967      */
27968     displayMsg : 'Displaying {0} - {1} of {2}',
27969     /**
27970      * @cfg {String} emptyMsg
27971      * The message to display when no records are found (defaults to "No data to display")
27972      */
27973     emptyMsg : 'No data to display',
27974     /**
27975      * Customizable piece of the default paging text (defaults to "Page")
27976      * @type String
27977      */
27978     beforePageText : "Page",
27979     /**
27980      * Customizable piece of the default paging text (defaults to "of %0")
27981      * @type String
27982      */
27983     afterPageText : "of {0}",
27984     /**
27985      * Customizable piece of the default paging text (defaults to "First Page")
27986      * @type String
27987      */
27988     firstText : "First Page",
27989     /**
27990      * Customizable piece of the default paging text (defaults to "Previous Page")
27991      * @type String
27992      */
27993     prevText : "Previous Page",
27994     /**
27995      * Customizable piece of the default paging text (defaults to "Next Page")
27996      * @type String
27997      */
27998     nextText : "Next Page",
27999     /**
28000      * Customizable piece of the default paging text (defaults to "Last Page")
28001      * @type String
28002      */
28003     lastText : "Last Page",
28004     /**
28005      * Customizable piece of the default paging text (defaults to "Refresh")
28006      * @type String
28007      */
28008     refreshText : "Refresh",
28009
28010     buttons : false,
28011     // private
28012     onRender : function(ct, position) 
28013     {
28014         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28015         this.navgroup.parentId = this.id;
28016         this.navgroup.onRender(this.el, null);
28017         // add the buttons to the navgroup
28018         
28019         if(this.displayInfo){
28020             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28021             this.displayEl = this.el.select('.x-paging-info', true).first();
28022 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28023 //            this.displayEl = navel.el.select('span',true).first();
28024         }
28025         
28026         var _this = this;
28027         
28028         if(this.buttons){
28029             Roo.each(_this.buttons, function(e){ // this might need to use render????
28030                Roo.factory(e).render(_this.el);
28031             });
28032         }
28033             
28034         Roo.each(_this.toolbarItems, function(e) {
28035             _this.navgroup.addItem(e);
28036         });
28037         
28038         
28039         this.first = this.navgroup.addItem({
28040             tooltip: this.firstText,
28041             cls: "prev btn-outline-secondary",
28042             html : ' <i class="fa fa-step-backward"></i>',
28043             disabled: true,
28044             preventDefault: true,
28045             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28046         });
28047         
28048         this.prev =  this.navgroup.addItem({
28049             tooltip: this.prevText,
28050             cls: "prev btn-outline-secondary",
28051             html : ' <i class="fa fa-backward"></i>',
28052             disabled: true,
28053             preventDefault: true,
28054             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28055         });
28056     //this.addSeparator();
28057         
28058         
28059         var field = this.navgroup.addItem( {
28060             tagtype : 'span',
28061             cls : 'x-paging-position  btn-outline-secondary',
28062              disabled: true,
28063             html : this.beforePageText  +
28064                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28065                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28066          } ); //?? escaped?
28067         
28068         this.field = field.el.select('input', true).first();
28069         this.field.on("keydown", this.onPagingKeydown, this);
28070         this.field.on("focus", function(){this.dom.select();});
28071     
28072     
28073         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28074         //this.field.setHeight(18);
28075         //this.addSeparator();
28076         this.next = this.navgroup.addItem({
28077             tooltip: this.nextText,
28078             cls: "next btn-outline-secondary",
28079             html : ' <i class="fa fa-forward"></i>',
28080             disabled: true,
28081             preventDefault: true,
28082             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28083         });
28084         this.last = this.navgroup.addItem({
28085             tooltip: this.lastText,
28086             html : ' <i class="fa fa-step-forward"></i>',
28087             cls: "next btn-outline-secondary",
28088             disabled: true,
28089             preventDefault: true,
28090             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28091         });
28092     //this.addSeparator();
28093         this.loading = this.navgroup.addItem({
28094             tooltip: this.refreshText,
28095             cls: "btn-outline-secondary",
28096             html : ' <i class="fa fa-refresh"></i>',
28097             preventDefault: true,
28098             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28099         });
28100         
28101     },
28102
28103     // private
28104     updateInfo : function(){
28105         if(this.displayEl){
28106             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28107             var msg = count == 0 ?
28108                 this.emptyMsg :
28109                 String.format(
28110                     this.displayMsg,
28111                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28112                 );
28113             this.displayEl.update(msg);
28114         }
28115     },
28116
28117     // private
28118     onLoad : function(ds, r, o)
28119     {
28120         this.cursor = o.params && o.params.start ? o.params.start : 0;
28121         
28122         var d = this.getPageData(),
28123             ap = d.activePage,
28124             ps = d.pages;
28125         
28126         
28127         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28128         this.field.dom.value = ap;
28129         this.first.setDisabled(ap == 1);
28130         this.prev.setDisabled(ap == 1);
28131         this.next.setDisabled(ap == ps);
28132         this.last.setDisabled(ap == ps);
28133         this.loading.enable();
28134         this.updateInfo();
28135     },
28136
28137     // private
28138     getPageData : function(){
28139         var total = this.ds.getTotalCount();
28140         return {
28141             total : total,
28142             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28143             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28144         };
28145     },
28146
28147     // private
28148     onLoadError : function(){
28149         this.loading.enable();
28150     },
28151
28152     // private
28153     onPagingKeydown : function(e){
28154         var k = e.getKey();
28155         var d = this.getPageData();
28156         if(k == e.RETURN){
28157             var v = this.field.dom.value, pageNum;
28158             if(!v || isNaN(pageNum = parseInt(v, 10))){
28159                 this.field.dom.value = d.activePage;
28160                 return;
28161             }
28162             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28163             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28164             e.stopEvent();
28165         }
28166         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))
28167         {
28168           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28169           this.field.dom.value = pageNum;
28170           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28171           e.stopEvent();
28172         }
28173         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28174         {
28175           var v = this.field.dom.value, pageNum; 
28176           var increment = (e.shiftKey) ? 10 : 1;
28177           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28178                 increment *= -1;
28179           }
28180           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28181             this.field.dom.value = d.activePage;
28182             return;
28183           }
28184           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28185           {
28186             this.field.dom.value = parseInt(v, 10) + increment;
28187             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28188             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28189           }
28190           e.stopEvent();
28191         }
28192     },
28193
28194     // private
28195     beforeLoad : function(){
28196         if(this.loading){
28197             this.loading.disable();
28198         }
28199     },
28200
28201     // private
28202     onClick : function(which){
28203         
28204         var ds = this.ds;
28205         if (!ds) {
28206             return;
28207         }
28208         
28209         switch(which){
28210             case "first":
28211                 ds.load({params:{start: 0, limit: this.pageSize}});
28212             break;
28213             case "prev":
28214                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28215             break;
28216             case "next":
28217                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28218             break;
28219             case "last":
28220                 var total = ds.getTotalCount();
28221                 var extra = total % this.pageSize;
28222                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28223                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28224             break;
28225             case "refresh":
28226                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28227             break;
28228         }
28229     },
28230
28231     /**
28232      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28233      * @param {Roo.data.Store} store The data store to unbind
28234      */
28235     unbind : function(ds){
28236         ds.un("beforeload", this.beforeLoad, this);
28237         ds.un("load", this.onLoad, this);
28238         ds.un("loadexception", this.onLoadError, this);
28239         ds.un("remove", this.updateInfo, this);
28240         ds.un("add", this.updateInfo, this);
28241         this.ds = undefined;
28242     },
28243
28244     /**
28245      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28246      * @param {Roo.data.Store} store The data store to bind
28247      */
28248     bind : function(ds){
28249         ds.on("beforeload", this.beforeLoad, this);
28250         ds.on("load", this.onLoad, this);
28251         ds.on("loadexception", this.onLoadError, this);
28252         ds.on("remove", this.updateInfo, this);
28253         ds.on("add", this.updateInfo, this);
28254         this.ds = ds;
28255     }
28256 });/*
28257  * - LGPL
28258  *
28259  * element
28260  * 
28261  */
28262
28263 /**
28264  * @class Roo.bootstrap.MessageBar
28265  * @extends Roo.bootstrap.Component
28266  * Bootstrap MessageBar class
28267  * @cfg {String} html contents of the MessageBar
28268  * @cfg {String} weight (info | success | warning | danger) default info
28269  * @cfg {String} beforeClass insert the bar before the given class
28270  * @cfg {Boolean} closable (true | false) default false
28271  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28272  * 
28273  * @constructor
28274  * Create a new Element
28275  * @param {Object} config The config object
28276  */
28277
28278 Roo.bootstrap.MessageBar = function(config){
28279     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28280 };
28281
28282 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28283     
28284     html: '',
28285     weight: 'info',
28286     closable: false,
28287     fixed: false,
28288     beforeClass: 'bootstrap-sticky-wrap',
28289     
28290     getAutoCreate : function(){
28291         
28292         var cfg = {
28293             tag: 'div',
28294             cls: 'alert alert-dismissable alert-' + this.weight,
28295             cn: [
28296                 {
28297                     tag: 'span',
28298                     cls: 'message',
28299                     html: this.html || ''
28300                 }
28301             ]
28302         };
28303         
28304         if(this.fixed){
28305             cfg.cls += ' alert-messages-fixed';
28306         }
28307         
28308         if(this.closable){
28309             cfg.cn.push({
28310                 tag: 'button',
28311                 cls: 'close',
28312                 html: 'x'
28313             });
28314         }
28315         
28316         return cfg;
28317     },
28318     
28319     onRender : function(ct, position)
28320     {
28321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28322         
28323         if(!this.el){
28324             var cfg = Roo.apply({},  this.getAutoCreate());
28325             cfg.id = Roo.id();
28326             
28327             if (this.cls) {
28328                 cfg.cls += ' ' + this.cls;
28329             }
28330             if (this.style) {
28331                 cfg.style = this.style;
28332             }
28333             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28334             
28335             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28336         }
28337         
28338         this.el.select('>button.close').on('click', this.hide, this);
28339         
28340     },
28341     
28342     show : function()
28343     {
28344         if (!this.rendered) {
28345             this.render();
28346         }
28347         
28348         this.el.show();
28349         
28350         this.fireEvent('show', this);
28351         
28352     },
28353     
28354     hide : function()
28355     {
28356         if (!this.rendered) {
28357             this.render();
28358         }
28359         
28360         this.el.hide();
28361         
28362         this.fireEvent('hide', this);
28363     },
28364     
28365     update : function()
28366     {
28367 //        var e = this.el.dom.firstChild;
28368 //        
28369 //        if(this.closable){
28370 //            e = e.nextSibling;
28371 //        }
28372 //        
28373 //        e.data = this.html || '';
28374
28375         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28376     }
28377    
28378 });
28379
28380  
28381
28382      /*
28383  * - LGPL
28384  *
28385  * Graph
28386  * 
28387  */
28388
28389
28390 /**
28391  * @class Roo.bootstrap.Graph
28392  * @extends Roo.bootstrap.Component
28393  * Bootstrap Graph class
28394 > Prameters
28395  -sm {number} sm 4
28396  -md {number} md 5
28397  @cfg {String} graphtype  bar | vbar | pie
28398  @cfg {number} g_x coodinator | centre x (pie)
28399  @cfg {number} g_y coodinator | centre y (pie)
28400  @cfg {number} g_r radius (pie)
28401  @cfg {number} g_height height of the chart (respected by all elements in the set)
28402  @cfg {number} g_width width of the chart (respected by all elements in the set)
28403  @cfg {Object} title The title of the chart
28404     
28405  -{Array}  values
28406  -opts (object) options for the chart 
28407      o {
28408      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28409      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28410      o vgutter (number)
28411      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.
28412      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28413      o to
28414      o stretch (boolean)
28415      o }
28416  -opts (object) options for the pie
28417      o{
28418      o cut
28419      o startAngle (number)
28420      o endAngle (number)
28421      } 
28422  *
28423  * @constructor
28424  * Create a new Input
28425  * @param {Object} config The config object
28426  */
28427
28428 Roo.bootstrap.Graph = function(config){
28429     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28430     
28431     this.addEvents({
28432         // img events
28433         /**
28434          * @event click
28435          * The img click event for the img.
28436          * @param {Roo.EventObject} e
28437          */
28438         "click" : true
28439     });
28440 };
28441
28442 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28443     
28444     sm: 4,
28445     md: 5,
28446     graphtype: 'bar',
28447     g_height: 250,
28448     g_width: 400,
28449     g_x: 50,
28450     g_y: 50,
28451     g_r: 30,
28452     opts:{
28453         //g_colors: this.colors,
28454         g_type: 'soft',
28455         g_gutter: '20%'
28456
28457     },
28458     title : false,
28459
28460     getAutoCreate : function(){
28461         
28462         var cfg = {
28463             tag: 'div',
28464             html : null
28465         };
28466         
28467         
28468         return  cfg;
28469     },
28470
28471     onRender : function(ct,position){
28472         
28473         
28474         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28475         
28476         if (typeof(Raphael) == 'undefined') {
28477             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28478             return;
28479         }
28480         
28481         this.raphael = Raphael(this.el.dom);
28482         
28483                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28484                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28485                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28486                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28487                 /*
28488                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28489                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28490                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28491                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28492                 
28493                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28494                 r.barchart(330, 10, 300, 220, data1);
28495                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28496                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28497                 */
28498                 
28499                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28500                 // r.barchart(30, 30, 560, 250,  xdata, {
28501                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28502                 //     axis : "0 0 1 1",
28503                 //     axisxlabels :  xdata
28504                 //     //yvalues : cols,
28505                    
28506                 // });
28507 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28508 //        
28509 //        this.load(null,xdata,{
28510 //                axis : "0 0 1 1",
28511 //                axisxlabels :  xdata
28512 //                });
28513
28514     },
28515
28516     load : function(graphtype,xdata,opts)
28517     {
28518         this.raphael.clear();
28519         if(!graphtype) {
28520             graphtype = this.graphtype;
28521         }
28522         if(!opts){
28523             opts = this.opts;
28524         }
28525         var r = this.raphael,
28526             fin = function () {
28527                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28528             },
28529             fout = function () {
28530                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28531             },
28532             pfin = function() {
28533                 this.sector.stop();
28534                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28535
28536                 if (this.label) {
28537                     this.label[0].stop();
28538                     this.label[0].attr({ r: 7.5 });
28539                     this.label[1].attr({ "font-weight": 800 });
28540                 }
28541             },
28542             pfout = function() {
28543                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28544
28545                 if (this.label) {
28546                     this.label[0].animate({ r: 5 }, 500, "bounce");
28547                     this.label[1].attr({ "font-weight": 400 });
28548                 }
28549             };
28550
28551         switch(graphtype){
28552             case 'bar':
28553                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28554                 break;
28555             case 'hbar':
28556                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28557                 break;
28558             case 'pie':
28559 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28560 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28561 //            
28562                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28563                 
28564                 break;
28565
28566         }
28567         
28568         if(this.title){
28569             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28570         }
28571         
28572     },
28573     
28574     setTitle: function(o)
28575     {
28576         this.title = o;
28577     },
28578     
28579     initEvents: function() {
28580         
28581         if(!this.href){
28582             this.el.on('click', this.onClick, this);
28583         }
28584     },
28585     
28586     onClick : function(e)
28587     {
28588         Roo.log('img onclick');
28589         this.fireEvent('click', this, e);
28590     }
28591    
28592 });
28593
28594  
28595 /*
28596  * - LGPL
28597  *
28598  * numberBox
28599  * 
28600  */
28601 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28602
28603 /**
28604  * @class Roo.bootstrap.dash.NumberBox
28605  * @extends Roo.bootstrap.Component
28606  * Bootstrap NumberBox class
28607  * @cfg {String} headline Box headline
28608  * @cfg {String} content Box content
28609  * @cfg {String} icon Box icon
28610  * @cfg {String} footer Footer text
28611  * @cfg {String} fhref Footer href
28612  * 
28613  * @constructor
28614  * Create a new NumberBox
28615  * @param {Object} config The config object
28616  */
28617
28618
28619 Roo.bootstrap.dash.NumberBox = function(config){
28620     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28621     
28622 };
28623
28624 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28625     
28626     headline : '',
28627     content : '',
28628     icon : '',
28629     footer : '',
28630     fhref : '',
28631     ficon : '',
28632     
28633     getAutoCreate : function(){
28634         
28635         var cfg = {
28636             tag : 'div',
28637             cls : 'small-box ',
28638             cn : [
28639                 {
28640                     tag : 'div',
28641                     cls : 'inner',
28642                     cn :[
28643                         {
28644                             tag : 'h3',
28645                             cls : 'roo-headline',
28646                             html : this.headline
28647                         },
28648                         {
28649                             tag : 'p',
28650                             cls : 'roo-content',
28651                             html : this.content
28652                         }
28653                     ]
28654                 }
28655             ]
28656         };
28657         
28658         if(this.icon){
28659             cfg.cn.push({
28660                 tag : 'div',
28661                 cls : 'icon',
28662                 cn :[
28663                     {
28664                         tag : 'i',
28665                         cls : 'ion ' + this.icon
28666                     }
28667                 ]
28668             });
28669         }
28670         
28671         if(this.footer){
28672             var footer = {
28673                 tag : 'a',
28674                 cls : 'small-box-footer',
28675                 href : this.fhref || '#',
28676                 html : this.footer
28677             };
28678             
28679             cfg.cn.push(footer);
28680             
28681         }
28682         
28683         return  cfg;
28684     },
28685
28686     onRender : function(ct,position){
28687         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28688
28689
28690        
28691                 
28692     },
28693
28694     setHeadline: function (value)
28695     {
28696         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28697     },
28698     
28699     setFooter: function (value, href)
28700     {
28701         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28702         
28703         if(href){
28704             this.el.select('a.small-box-footer',true).first().attr('href', href);
28705         }
28706         
28707     },
28708
28709     setContent: function (value)
28710     {
28711         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28712     },
28713
28714     initEvents: function() 
28715     {   
28716         
28717     }
28718     
28719 });
28720
28721  
28722 /*
28723  * - LGPL
28724  *
28725  * TabBox
28726  * 
28727  */
28728 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28729
28730 /**
28731  * @class Roo.bootstrap.dash.TabBox
28732  * @extends Roo.bootstrap.Component
28733  * Bootstrap TabBox class
28734  * @cfg {String} title Title of the TabBox
28735  * @cfg {String} icon Icon of the TabBox
28736  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28737  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28738  * 
28739  * @constructor
28740  * Create a new TabBox
28741  * @param {Object} config The config object
28742  */
28743
28744
28745 Roo.bootstrap.dash.TabBox = function(config){
28746     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28747     this.addEvents({
28748         // raw events
28749         /**
28750          * @event addpane
28751          * When a pane is added
28752          * @param {Roo.bootstrap.dash.TabPane} pane
28753          */
28754         "addpane" : true,
28755         /**
28756          * @event activatepane
28757          * When a pane is activated
28758          * @param {Roo.bootstrap.dash.TabPane} pane
28759          */
28760         "activatepane" : true
28761         
28762          
28763     });
28764     
28765     this.panes = [];
28766 };
28767
28768 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28769
28770     title : '',
28771     icon : false,
28772     showtabs : true,
28773     tabScrollable : false,
28774     
28775     getChildContainer : function()
28776     {
28777         return this.el.select('.tab-content', true).first();
28778     },
28779     
28780     getAutoCreate : function(){
28781         
28782         var header = {
28783             tag: 'li',
28784             cls: 'pull-left header',
28785             html: this.title,
28786             cn : []
28787         };
28788         
28789         if(this.icon){
28790             header.cn.push({
28791                 tag: 'i',
28792                 cls: 'fa ' + this.icon
28793             });
28794         }
28795         
28796         var h = {
28797             tag: 'ul',
28798             cls: 'nav nav-tabs pull-right',
28799             cn: [
28800                 header
28801             ]
28802         };
28803         
28804         if(this.tabScrollable){
28805             h = {
28806                 tag: 'div',
28807                 cls: 'tab-header',
28808                 cn: [
28809                     {
28810                         tag: 'ul',
28811                         cls: 'nav nav-tabs pull-right',
28812                         cn: [
28813                             header
28814                         ]
28815                     }
28816                 ]
28817             };
28818         }
28819         
28820         var cfg = {
28821             tag: 'div',
28822             cls: 'nav-tabs-custom',
28823             cn: [
28824                 h,
28825                 {
28826                     tag: 'div',
28827                     cls: 'tab-content no-padding',
28828                     cn: []
28829                 }
28830             ]
28831         };
28832
28833         return  cfg;
28834     },
28835     initEvents : function()
28836     {
28837         //Roo.log('add add pane handler');
28838         this.on('addpane', this.onAddPane, this);
28839     },
28840      /**
28841      * Updates the box title
28842      * @param {String} html to set the title to.
28843      */
28844     setTitle : function(value)
28845     {
28846         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28847     },
28848     onAddPane : function(pane)
28849     {
28850         this.panes.push(pane);
28851         //Roo.log('addpane');
28852         //Roo.log(pane);
28853         // tabs are rendere left to right..
28854         if(!this.showtabs){
28855             return;
28856         }
28857         
28858         var ctr = this.el.select('.nav-tabs', true).first();
28859          
28860          
28861         var existing = ctr.select('.nav-tab',true);
28862         var qty = existing.getCount();;
28863         
28864         
28865         var tab = ctr.createChild({
28866             tag : 'li',
28867             cls : 'nav-tab' + (qty ? '' : ' active'),
28868             cn : [
28869                 {
28870                     tag : 'a',
28871                     href:'#',
28872                     html : pane.title
28873                 }
28874             ]
28875         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28876         pane.tab = tab;
28877         
28878         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28879         if (!qty) {
28880             pane.el.addClass('active');
28881         }
28882         
28883                 
28884     },
28885     onTabClick : function(ev,un,ob,pane)
28886     {
28887         //Roo.log('tab - prev default');
28888         ev.preventDefault();
28889         
28890         
28891         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28892         pane.tab.addClass('active');
28893         //Roo.log(pane.title);
28894         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28895         // technically we should have a deactivate event.. but maybe add later.
28896         // and it should not de-activate the selected tab...
28897         this.fireEvent('activatepane', pane);
28898         pane.el.addClass('active');
28899         pane.fireEvent('activate');
28900         
28901         
28902     },
28903     
28904     getActivePane : function()
28905     {
28906         var r = false;
28907         Roo.each(this.panes, function(p) {
28908             if(p.el.hasClass('active')){
28909                 r = p;
28910                 return false;
28911             }
28912             
28913             return;
28914         });
28915         
28916         return r;
28917     }
28918     
28919     
28920 });
28921
28922  
28923 /*
28924  * - LGPL
28925  *
28926  * Tab pane
28927  * 
28928  */
28929 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28930 /**
28931  * @class Roo.bootstrap.TabPane
28932  * @extends Roo.bootstrap.Component
28933  * Bootstrap TabPane class
28934  * @cfg {Boolean} active (false | true) Default false
28935  * @cfg {String} title title of panel
28936
28937  * 
28938  * @constructor
28939  * Create a new TabPane
28940  * @param {Object} config The config object
28941  */
28942
28943 Roo.bootstrap.dash.TabPane = function(config){
28944     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28945     
28946     this.addEvents({
28947         // raw events
28948         /**
28949          * @event activate
28950          * When a pane is activated
28951          * @param {Roo.bootstrap.dash.TabPane} pane
28952          */
28953         "activate" : true
28954          
28955     });
28956 };
28957
28958 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28959     
28960     active : false,
28961     title : '',
28962     
28963     // the tabBox that this is attached to.
28964     tab : false,
28965      
28966     getAutoCreate : function() 
28967     {
28968         var cfg = {
28969             tag: 'div',
28970             cls: 'tab-pane'
28971         };
28972         
28973         if(this.active){
28974             cfg.cls += ' active';
28975         }
28976         
28977         return cfg;
28978     },
28979     initEvents  : function()
28980     {
28981         //Roo.log('trigger add pane handler');
28982         this.parent().fireEvent('addpane', this)
28983     },
28984     
28985      /**
28986      * Updates the tab title 
28987      * @param {String} html to set the title to.
28988      */
28989     setTitle: function(str)
28990     {
28991         if (!this.tab) {
28992             return;
28993         }
28994         this.title = str;
28995         this.tab.select('a', true).first().dom.innerHTML = str;
28996         
28997     }
28998     
28999     
29000     
29001 });
29002
29003  
29004
29005
29006  /*
29007  * - LGPL
29008  *
29009  * menu
29010  * 
29011  */
29012 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29013
29014 /**
29015  * @class Roo.bootstrap.menu.Menu
29016  * @extends Roo.bootstrap.Component
29017  * Bootstrap Menu class - container for Menu
29018  * @cfg {String} html Text of the menu
29019  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29020  * @cfg {String} icon Font awesome icon
29021  * @cfg {String} pos Menu align to (top | bottom) default bottom
29022  * 
29023  * 
29024  * @constructor
29025  * Create a new Menu
29026  * @param {Object} config The config object
29027  */
29028
29029
29030 Roo.bootstrap.menu.Menu = function(config){
29031     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29032     
29033     this.addEvents({
29034         /**
29035          * @event beforeshow
29036          * Fires before this menu is displayed
29037          * @param {Roo.bootstrap.menu.Menu} this
29038          */
29039         beforeshow : true,
29040         /**
29041          * @event beforehide
29042          * Fires before this menu is hidden
29043          * @param {Roo.bootstrap.menu.Menu} this
29044          */
29045         beforehide : true,
29046         /**
29047          * @event show
29048          * Fires after this menu is displayed
29049          * @param {Roo.bootstrap.menu.Menu} this
29050          */
29051         show : true,
29052         /**
29053          * @event hide
29054          * Fires after this menu is hidden
29055          * @param {Roo.bootstrap.menu.Menu} this
29056          */
29057         hide : true,
29058         /**
29059          * @event click
29060          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29061          * @param {Roo.bootstrap.menu.Menu} this
29062          * @param {Roo.EventObject} e
29063          */
29064         click : true
29065     });
29066     
29067 };
29068
29069 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29070     
29071     submenu : false,
29072     html : '',
29073     weight : 'default',
29074     icon : false,
29075     pos : 'bottom',
29076     
29077     
29078     getChildContainer : function() {
29079         if(this.isSubMenu){
29080             return this.el;
29081         }
29082         
29083         return this.el.select('ul.dropdown-menu', true).first();  
29084     },
29085     
29086     getAutoCreate : function()
29087     {
29088         var text = [
29089             {
29090                 tag : 'span',
29091                 cls : 'roo-menu-text',
29092                 html : this.html
29093             }
29094         ];
29095         
29096         if(this.icon){
29097             text.unshift({
29098                 tag : 'i',
29099                 cls : 'fa ' + this.icon
29100             })
29101         }
29102         
29103         
29104         var cfg = {
29105             tag : 'div',
29106             cls : 'btn-group',
29107             cn : [
29108                 {
29109                     tag : 'button',
29110                     cls : 'dropdown-button btn btn-' + this.weight,
29111                     cn : text
29112                 },
29113                 {
29114                     tag : 'button',
29115                     cls : 'dropdown-toggle btn btn-' + this.weight,
29116                     cn : [
29117                         {
29118                             tag : 'span',
29119                             cls : 'caret'
29120                         }
29121                     ]
29122                 },
29123                 {
29124                     tag : 'ul',
29125                     cls : 'dropdown-menu'
29126                 }
29127             ]
29128             
29129         };
29130         
29131         if(this.pos == 'top'){
29132             cfg.cls += ' dropup';
29133         }
29134         
29135         if(this.isSubMenu){
29136             cfg = {
29137                 tag : 'ul',
29138                 cls : 'dropdown-menu'
29139             }
29140         }
29141         
29142         return cfg;
29143     },
29144     
29145     onRender : function(ct, position)
29146     {
29147         this.isSubMenu = ct.hasClass('dropdown-submenu');
29148         
29149         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29150     },
29151     
29152     initEvents : function() 
29153     {
29154         if(this.isSubMenu){
29155             return;
29156         }
29157         
29158         this.hidden = true;
29159         
29160         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29161         this.triggerEl.on('click', this.onTriggerPress, this);
29162         
29163         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29164         this.buttonEl.on('click', this.onClick, this);
29165         
29166     },
29167     
29168     list : function()
29169     {
29170         if(this.isSubMenu){
29171             return this.el;
29172         }
29173         
29174         return this.el.select('ul.dropdown-menu', true).first();
29175     },
29176     
29177     onClick : function(e)
29178     {
29179         this.fireEvent("click", this, e);
29180     },
29181     
29182     onTriggerPress  : function(e)
29183     {   
29184         if (this.isVisible()) {
29185             this.hide();
29186         } else {
29187             this.show();
29188         }
29189     },
29190     
29191     isVisible : function(){
29192         return !this.hidden;
29193     },
29194     
29195     show : function()
29196     {
29197         this.fireEvent("beforeshow", this);
29198         
29199         this.hidden = false;
29200         this.el.addClass('open');
29201         
29202         Roo.get(document).on("mouseup", this.onMouseUp, this);
29203         
29204         this.fireEvent("show", this);
29205         
29206         
29207     },
29208     
29209     hide : function()
29210     {
29211         this.fireEvent("beforehide", this);
29212         
29213         this.hidden = true;
29214         this.el.removeClass('open');
29215         
29216         Roo.get(document).un("mouseup", this.onMouseUp);
29217         
29218         this.fireEvent("hide", this);
29219     },
29220     
29221     onMouseUp : function()
29222     {
29223         this.hide();
29224     }
29225     
29226 });
29227
29228  
29229  /*
29230  * - LGPL
29231  *
29232  * menu item
29233  * 
29234  */
29235 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29236
29237 /**
29238  * @class Roo.bootstrap.menu.Item
29239  * @extends Roo.bootstrap.Component
29240  * Bootstrap MenuItem class
29241  * @cfg {Boolean} submenu (true | false) default false
29242  * @cfg {String} html text of the item
29243  * @cfg {String} href the link
29244  * @cfg {Boolean} disable (true | false) default false
29245  * @cfg {Boolean} preventDefault (true | false) default true
29246  * @cfg {String} icon Font awesome icon
29247  * @cfg {String} pos Submenu align to (left | right) default right 
29248  * 
29249  * 
29250  * @constructor
29251  * Create a new Item
29252  * @param {Object} config The config object
29253  */
29254
29255
29256 Roo.bootstrap.menu.Item = function(config){
29257     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29258     this.addEvents({
29259         /**
29260          * @event mouseover
29261          * Fires when the mouse is hovering over this menu
29262          * @param {Roo.bootstrap.menu.Item} this
29263          * @param {Roo.EventObject} e
29264          */
29265         mouseover : true,
29266         /**
29267          * @event mouseout
29268          * Fires when the mouse exits this menu
29269          * @param {Roo.bootstrap.menu.Item} this
29270          * @param {Roo.EventObject} e
29271          */
29272         mouseout : true,
29273         // raw events
29274         /**
29275          * @event click
29276          * The raw click event for the entire grid.
29277          * @param {Roo.EventObject} e
29278          */
29279         click : true
29280     });
29281 };
29282
29283 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29284     
29285     submenu : false,
29286     href : '',
29287     html : '',
29288     preventDefault: true,
29289     disable : false,
29290     icon : false,
29291     pos : 'right',
29292     
29293     getAutoCreate : function()
29294     {
29295         var text = [
29296             {
29297                 tag : 'span',
29298                 cls : 'roo-menu-item-text',
29299                 html : this.html
29300             }
29301         ];
29302         
29303         if(this.icon){
29304             text.unshift({
29305                 tag : 'i',
29306                 cls : 'fa ' + this.icon
29307             })
29308         }
29309         
29310         var cfg = {
29311             tag : 'li',
29312             cn : [
29313                 {
29314                     tag : 'a',
29315                     href : this.href || '#',
29316                     cn : text
29317                 }
29318             ]
29319         };
29320         
29321         if(this.disable){
29322             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29323         }
29324         
29325         if(this.submenu){
29326             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29327             
29328             if(this.pos == 'left'){
29329                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29330             }
29331         }
29332         
29333         return cfg;
29334     },
29335     
29336     initEvents : function() 
29337     {
29338         this.el.on('mouseover', this.onMouseOver, this);
29339         this.el.on('mouseout', this.onMouseOut, this);
29340         
29341         this.el.select('a', true).first().on('click', this.onClick, this);
29342         
29343     },
29344     
29345     onClick : function(e)
29346     {
29347         if(this.preventDefault){
29348             e.preventDefault();
29349         }
29350         
29351         this.fireEvent("click", this, e);
29352     },
29353     
29354     onMouseOver : function(e)
29355     {
29356         if(this.submenu && this.pos == 'left'){
29357             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29358         }
29359         
29360         this.fireEvent("mouseover", this, e);
29361     },
29362     
29363     onMouseOut : function(e)
29364     {
29365         this.fireEvent("mouseout", this, e);
29366     }
29367 });
29368
29369  
29370
29371  /*
29372  * - LGPL
29373  *
29374  * menu separator
29375  * 
29376  */
29377 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29378
29379 /**
29380  * @class Roo.bootstrap.menu.Separator
29381  * @extends Roo.bootstrap.Component
29382  * Bootstrap Separator class
29383  * 
29384  * @constructor
29385  * Create a new Separator
29386  * @param {Object} config The config object
29387  */
29388
29389
29390 Roo.bootstrap.menu.Separator = function(config){
29391     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29392 };
29393
29394 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29395     
29396     getAutoCreate : function(){
29397         var cfg = {
29398             tag : 'li',
29399             cls: 'dropdown-divider divider'
29400         };
29401         
29402         return cfg;
29403     }
29404    
29405 });
29406
29407  
29408
29409  /*
29410  * - LGPL
29411  *
29412  * Tooltip
29413  * 
29414  */
29415
29416 /**
29417  * @class Roo.bootstrap.Tooltip
29418  * Bootstrap Tooltip class
29419  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29420  * to determine which dom element triggers the tooltip.
29421  * 
29422  * It needs to add support for additional attributes like tooltip-position
29423  * 
29424  * @constructor
29425  * Create a new Toolti
29426  * @param {Object} config The config object
29427  */
29428
29429 Roo.bootstrap.Tooltip = function(config){
29430     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29431     
29432     this.alignment = Roo.bootstrap.Tooltip.alignment;
29433     
29434     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29435         this.alignment = config.alignment;
29436     }
29437     
29438 };
29439
29440 Roo.apply(Roo.bootstrap.Tooltip, {
29441     /**
29442      * @function init initialize tooltip monitoring.
29443      * @static
29444      */
29445     currentEl : false,
29446     currentTip : false,
29447     currentRegion : false,
29448     
29449     //  init : delay?
29450     
29451     init : function()
29452     {
29453         Roo.get(document).on('mouseover', this.enter ,this);
29454         Roo.get(document).on('mouseout', this.leave, this);
29455          
29456         
29457         this.currentTip = new Roo.bootstrap.Tooltip();
29458     },
29459     
29460     enter : function(ev)
29461     {
29462         var dom = ev.getTarget();
29463         
29464         //Roo.log(['enter',dom]);
29465         var el = Roo.fly(dom);
29466         if (this.currentEl) {
29467             //Roo.log(dom);
29468             //Roo.log(this.currentEl);
29469             //Roo.log(this.currentEl.contains(dom));
29470             if (this.currentEl == el) {
29471                 return;
29472             }
29473             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29474                 return;
29475             }
29476
29477         }
29478         
29479         if (this.currentTip.el) {
29480             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29481         }    
29482         //Roo.log(ev);
29483         
29484         if(!el || el.dom == document){
29485             return;
29486         }
29487         
29488         var bindEl = el; 
29489         var pel = false;
29490         if (!el.attr('tooltip')) {
29491             pel = el.findParent("[tooltip]");
29492             if (pel) {
29493                 bindEl = Roo.get(pel);
29494             }
29495         }
29496         
29497        
29498         
29499         // you can not look for children, as if el is the body.. then everythign is the child..
29500         if (!pel && !el.attr('tooltip')) { //
29501             if (!el.select("[tooltip]").elements.length) {
29502                 return;
29503             }
29504             // is the mouse over this child...?
29505             bindEl = el.select("[tooltip]").first();
29506             var xy = ev.getXY();
29507             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29508                 //Roo.log("not in region.");
29509                 return;
29510             }
29511             //Roo.log("child element over..");
29512             
29513         }
29514         this.currentEl = el;
29515         this.currentTip.bind(bindEl);
29516         this.currentRegion = Roo.lib.Region.getRegion(dom);
29517         this.currentTip.enter();
29518         
29519     },
29520     leave : function(ev)
29521     {
29522         var dom = ev.getTarget();
29523         //Roo.log(['leave',dom]);
29524         if (!this.currentEl) {
29525             return;
29526         }
29527         
29528         
29529         if (dom != this.currentEl.dom) {
29530             return;
29531         }
29532         var xy = ev.getXY();
29533         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29534             return;
29535         }
29536         // only activate leave if mouse cursor is outside... bounding box..
29537         
29538         
29539         
29540         
29541         if (this.currentTip) {
29542             this.currentTip.leave();
29543         }
29544         //Roo.log('clear currentEl');
29545         this.currentEl = false;
29546         
29547         
29548     },
29549     alignment : {
29550         'left' : ['r-l', [-2,0], 'right'],
29551         'right' : ['l-r', [2,0], 'left'],
29552         'bottom' : ['t-b', [0,2], 'top'],
29553         'top' : [ 'b-t', [0,-2], 'bottom']
29554     }
29555     
29556 });
29557
29558
29559 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29560     
29561     
29562     bindEl : false,
29563     
29564     delay : null, // can be { show : 300 , hide: 500}
29565     
29566     timeout : null,
29567     
29568     hoverState : null, //???
29569     
29570     placement : 'bottom', 
29571     
29572     alignment : false,
29573     
29574     getAutoCreate : function(){
29575     
29576         var cfg = {
29577            cls : 'tooltip',   
29578            role : 'tooltip',
29579            cn : [
29580                 {
29581                     cls : 'tooltip-arrow arrow'
29582                 },
29583                 {
29584                     cls : 'tooltip-inner'
29585                 }
29586            ]
29587         };
29588         
29589         return cfg;
29590     },
29591     bind : function(el)
29592     {
29593         this.bindEl = el;
29594     },
29595     
29596     initEvents : function()
29597     {
29598         this.arrowEl = this.el.select('.arrow', true).first();
29599         this.innerEl = this.el.select('.tooltip-inner', true).first();
29600     },
29601     
29602     enter : function () {
29603        
29604         if (this.timeout != null) {
29605             clearTimeout(this.timeout);
29606         }
29607         
29608         this.hoverState = 'in';
29609          //Roo.log("enter - show");
29610         if (!this.delay || !this.delay.show) {
29611             this.show();
29612             return;
29613         }
29614         var _t = this;
29615         this.timeout = setTimeout(function () {
29616             if (_t.hoverState == 'in') {
29617                 _t.show();
29618             }
29619         }, this.delay.show);
29620     },
29621     leave : function()
29622     {
29623         clearTimeout(this.timeout);
29624     
29625         this.hoverState = 'out';
29626          if (!this.delay || !this.delay.hide) {
29627             this.hide();
29628             return;
29629         }
29630        
29631         var _t = this;
29632         this.timeout = setTimeout(function () {
29633             //Roo.log("leave - timeout");
29634             
29635             if (_t.hoverState == 'out') {
29636                 _t.hide();
29637                 Roo.bootstrap.Tooltip.currentEl = false;
29638             }
29639         }, delay);
29640     },
29641     
29642     show : function (msg)
29643     {
29644         if (!this.el) {
29645             this.render(document.body);
29646         }
29647         // set content.
29648         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29649         
29650         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29651         
29652         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29653         
29654         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29655                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29656         
29657         var placement = typeof this.placement == 'function' ?
29658             this.placement.call(this, this.el, on_el) :
29659             this.placement;
29660             
29661         var autoToken = /\s?auto?\s?/i;
29662         var autoPlace = autoToken.test(placement);
29663         if (autoPlace) {
29664             placement = placement.replace(autoToken, '') || 'top';
29665         }
29666         
29667         //this.el.detach()
29668         //this.el.setXY([0,0]);
29669         this.el.show();
29670         //this.el.dom.style.display='block';
29671         
29672         //this.el.appendTo(on_el);
29673         
29674         var p = this.getPosition();
29675         var box = this.el.getBox();
29676         
29677         if (autoPlace) {
29678             // fixme..
29679         }
29680         
29681         var align = this.alignment[placement];
29682         
29683         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29684         
29685         if(placement == 'top' || placement == 'bottom'){
29686             if(xy[0] < 0){
29687                 placement = 'right';
29688             }
29689             
29690             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29691                 placement = 'left';
29692             }
29693             
29694             var scroll = Roo.select('body', true).first().getScroll();
29695             
29696             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29697                 placement = 'top';
29698             }
29699             
29700             align = this.alignment[placement];
29701             
29702             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29703             
29704         }
29705         
29706         var elems = document.getElementsByTagName('div');
29707         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29708         for (var i = 0; i < elems.length; i++) {
29709           var zindex = Number.parseInt(
29710                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29711                 10
29712           );
29713           if (zindex > highest) {
29714             highest = zindex;
29715           }
29716         }
29717         
29718         
29719         
29720         this.el.dom.style.zIndex = highest;
29721         
29722         this.el.alignTo(this.bindEl, align[0],align[1]);
29723         //var arrow = this.el.select('.arrow',true).first();
29724         //arrow.set(align[2], 
29725         
29726         this.el.addClass(placement);
29727         this.el.addClass("bs-tooltip-"+ placement);
29728         
29729         this.el.addClass('in fade show');
29730         
29731         this.hoverState = null;
29732         
29733         if (this.el.hasClass('fade')) {
29734             // fade it?
29735         }
29736         
29737         
29738         
29739         
29740         
29741     },
29742     hide : function()
29743     {
29744          
29745         if (!this.el) {
29746             return;
29747         }
29748         //this.el.setXY([0,0]);
29749         this.el.removeClass(['show', 'in']);
29750         //this.el.hide();
29751         
29752     }
29753     
29754 });
29755  
29756
29757  /*
29758  * - LGPL
29759  *
29760  * Location Picker
29761  * 
29762  */
29763
29764 /**
29765  * @class Roo.bootstrap.LocationPicker
29766  * @extends Roo.bootstrap.Component
29767  * Bootstrap LocationPicker class
29768  * @cfg {Number} latitude Position when init default 0
29769  * @cfg {Number} longitude Position when init default 0
29770  * @cfg {Number} zoom default 15
29771  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29772  * @cfg {Boolean} mapTypeControl default false
29773  * @cfg {Boolean} disableDoubleClickZoom default false
29774  * @cfg {Boolean} scrollwheel default true
29775  * @cfg {Boolean} streetViewControl default false
29776  * @cfg {Number} radius default 0
29777  * @cfg {String} locationName
29778  * @cfg {Boolean} draggable default true
29779  * @cfg {Boolean} enableAutocomplete default false
29780  * @cfg {Boolean} enableReverseGeocode default true
29781  * @cfg {String} markerTitle
29782  * 
29783  * @constructor
29784  * Create a new LocationPicker
29785  * @param {Object} config The config object
29786  */
29787
29788
29789 Roo.bootstrap.LocationPicker = function(config){
29790     
29791     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29792     
29793     this.addEvents({
29794         /**
29795          * @event initial
29796          * Fires when the picker initialized.
29797          * @param {Roo.bootstrap.LocationPicker} this
29798          * @param {Google Location} location
29799          */
29800         initial : true,
29801         /**
29802          * @event positionchanged
29803          * Fires when the picker position changed.
29804          * @param {Roo.bootstrap.LocationPicker} this
29805          * @param {Google Location} location
29806          */
29807         positionchanged : true,
29808         /**
29809          * @event resize
29810          * Fires when the map resize.
29811          * @param {Roo.bootstrap.LocationPicker} this
29812          */
29813         resize : true,
29814         /**
29815          * @event show
29816          * Fires when the map show.
29817          * @param {Roo.bootstrap.LocationPicker} this
29818          */
29819         show : true,
29820         /**
29821          * @event hide
29822          * Fires when the map hide.
29823          * @param {Roo.bootstrap.LocationPicker} this
29824          */
29825         hide : true,
29826         /**
29827          * @event mapClick
29828          * Fires when click the map.
29829          * @param {Roo.bootstrap.LocationPicker} this
29830          * @param {Map event} e
29831          */
29832         mapClick : true,
29833         /**
29834          * @event mapRightClick
29835          * Fires when right click the map.
29836          * @param {Roo.bootstrap.LocationPicker} this
29837          * @param {Map event} e
29838          */
29839         mapRightClick : true,
29840         /**
29841          * @event markerClick
29842          * Fires when click the marker.
29843          * @param {Roo.bootstrap.LocationPicker} this
29844          * @param {Map event} e
29845          */
29846         markerClick : true,
29847         /**
29848          * @event markerRightClick
29849          * Fires when right click the marker.
29850          * @param {Roo.bootstrap.LocationPicker} this
29851          * @param {Map event} e
29852          */
29853         markerRightClick : true,
29854         /**
29855          * @event OverlayViewDraw
29856          * Fires when OverlayView Draw
29857          * @param {Roo.bootstrap.LocationPicker} this
29858          */
29859         OverlayViewDraw : true,
29860         /**
29861          * @event OverlayViewOnAdd
29862          * Fires when OverlayView Draw
29863          * @param {Roo.bootstrap.LocationPicker} this
29864          */
29865         OverlayViewOnAdd : true,
29866         /**
29867          * @event OverlayViewOnRemove
29868          * Fires when OverlayView Draw
29869          * @param {Roo.bootstrap.LocationPicker} this
29870          */
29871         OverlayViewOnRemove : true,
29872         /**
29873          * @event OverlayViewShow
29874          * Fires when OverlayView Draw
29875          * @param {Roo.bootstrap.LocationPicker} this
29876          * @param {Pixel} cpx
29877          */
29878         OverlayViewShow : true,
29879         /**
29880          * @event OverlayViewHide
29881          * Fires when OverlayView Draw
29882          * @param {Roo.bootstrap.LocationPicker} this
29883          */
29884         OverlayViewHide : true,
29885         /**
29886          * @event loadexception
29887          * Fires when load google lib failed.
29888          * @param {Roo.bootstrap.LocationPicker} this
29889          */
29890         loadexception : true
29891     });
29892         
29893 };
29894
29895 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29896     
29897     gMapContext: false,
29898     
29899     latitude: 0,
29900     longitude: 0,
29901     zoom: 15,
29902     mapTypeId: false,
29903     mapTypeControl: false,
29904     disableDoubleClickZoom: false,
29905     scrollwheel: true,
29906     streetViewControl: false,
29907     radius: 0,
29908     locationName: '',
29909     draggable: true,
29910     enableAutocomplete: false,
29911     enableReverseGeocode: true,
29912     markerTitle: '',
29913     
29914     getAutoCreate: function()
29915     {
29916
29917         var cfg = {
29918             tag: 'div',
29919             cls: 'roo-location-picker'
29920         };
29921         
29922         return cfg
29923     },
29924     
29925     initEvents: function(ct, position)
29926     {       
29927         if(!this.el.getWidth() || this.isApplied()){
29928             return;
29929         }
29930         
29931         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29932         
29933         this.initial();
29934     },
29935     
29936     initial: function()
29937     {
29938         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29939             this.fireEvent('loadexception', this);
29940             return;
29941         }
29942         
29943         if(!this.mapTypeId){
29944             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29945         }
29946         
29947         this.gMapContext = this.GMapContext();
29948         
29949         this.initOverlayView();
29950         
29951         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29952         
29953         var _this = this;
29954                 
29955         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29956             _this.setPosition(_this.gMapContext.marker.position);
29957         });
29958         
29959         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29960             _this.fireEvent('mapClick', this, event);
29961             
29962         });
29963
29964         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29965             _this.fireEvent('mapRightClick', this, event);
29966             
29967         });
29968         
29969         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29970             _this.fireEvent('markerClick', this, event);
29971             
29972         });
29973
29974         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29975             _this.fireEvent('markerRightClick', this, event);
29976             
29977         });
29978         
29979         this.setPosition(this.gMapContext.location);
29980         
29981         this.fireEvent('initial', this, this.gMapContext.location);
29982     },
29983     
29984     initOverlayView: function()
29985     {
29986         var _this = this;
29987         
29988         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29989             
29990             draw: function()
29991             {
29992                 _this.fireEvent('OverlayViewDraw', _this);
29993             },
29994             
29995             onAdd: function()
29996             {
29997                 _this.fireEvent('OverlayViewOnAdd', _this);
29998             },
29999             
30000             onRemove: function()
30001             {
30002                 _this.fireEvent('OverlayViewOnRemove', _this);
30003             },
30004             
30005             show: function(cpx)
30006             {
30007                 _this.fireEvent('OverlayViewShow', _this, cpx);
30008             },
30009             
30010             hide: function()
30011             {
30012                 _this.fireEvent('OverlayViewHide', _this);
30013             }
30014             
30015         });
30016     },
30017     
30018     fromLatLngToContainerPixel: function(event)
30019     {
30020         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30021     },
30022     
30023     isApplied: function() 
30024     {
30025         return this.getGmapContext() == false ? false : true;
30026     },
30027     
30028     getGmapContext: function() 
30029     {
30030         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30031     },
30032     
30033     GMapContext: function() 
30034     {
30035         var position = new google.maps.LatLng(this.latitude, this.longitude);
30036         
30037         var _map = new google.maps.Map(this.el.dom, {
30038             center: position,
30039             zoom: this.zoom,
30040             mapTypeId: this.mapTypeId,
30041             mapTypeControl: this.mapTypeControl,
30042             disableDoubleClickZoom: this.disableDoubleClickZoom,
30043             scrollwheel: this.scrollwheel,
30044             streetViewControl: this.streetViewControl,
30045             locationName: this.locationName,
30046             draggable: this.draggable,
30047             enableAutocomplete: this.enableAutocomplete,
30048             enableReverseGeocode: this.enableReverseGeocode
30049         });
30050         
30051         var _marker = new google.maps.Marker({
30052             position: position,
30053             map: _map,
30054             title: this.markerTitle,
30055             draggable: this.draggable
30056         });
30057         
30058         return {
30059             map: _map,
30060             marker: _marker,
30061             circle: null,
30062             location: position,
30063             radius: this.radius,
30064             locationName: this.locationName,
30065             addressComponents: {
30066                 formatted_address: null,
30067                 addressLine1: null,
30068                 addressLine2: null,
30069                 streetName: null,
30070                 streetNumber: null,
30071                 city: null,
30072                 district: null,
30073                 state: null,
30074                 stateOrProvince: null
30075             },
30076             settings: this,
30077             domContainer: this.el.dom,
30078             geodecoder: new google.maps.Geocoder()
30079         };
30080     },
30081     
30082     drawCircle: function(center, radius, options) 
30083     {
30084         if (this.gMapContext.circle != null) {
30085             this.gMapContext.circle.setMap(null);
30086         }
30087         if (radius > 0) {
30088             radius *= 1;
30089             options = Roo.apply({}, options, {
30090                 strokeColor: "#0000FF",
30091                 strokeOpacity: .35,
30092                 strokeWeight: 2,
30093                 fillColor: "#0000FF",
30094                 fillOpacity: .2
30095             });
30096             
30097             options.map = this.gMapContext.map;
30098             options.radius = radius;
30099             options.center = center;
30100             this.gMapContext.circle = new google.maps.Circle(options);
30101             return this.gMapContext.circle;
30102         }
30103         
30104         return null;
30105     },
30106     
30107     setPosition: function(location) 
30108     {
30109         this.gMapContext.location = location;
30110         this.gMapContext.marker.setPosition(location);
30111         this.gMapContext.map.panTo(location);
30112         this.drawCircle(location, this.gMapContext.radius, {});
30113         
30114         var _this = this;
30115         
30116         if (this.gMapContext.settings.enableReverseGeocode) {
30117             this.gMapContext.geodecoder.geocode({
30118                 latLng: this.gMapContext.location
30119             }, function(results, status) {
30120                 
30121                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30122                     _this.gMapContext.locationName = results[0].formatted_address;
30123                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30124                     
30125                     _this.fireEvent('positionchanged', this, location);
30126                 }
30127             });
30128             
30129             return;
30130         }
30131         
30132         this.fireEvent('positionchanged', this, location);
30133     },
30134     
30135     resize: function()
30136     {
30137         google.maps.event.trigger(this.gMapContext.map, "resize");
30138         
30139         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30140         
30141         this.fireEvent('resize', this);
30142     },
30143     
30144     setPositionByLatLng: function(latitude, longitude)
30145     {
30146         this.setPosition(new google.maps.LatLng(latitude, longitude));
30147     },
30148     
30149     getCurrentPosition: function() 
30150     {
30151         return {
30152             latitude: this.gMapContext.location.lat(),
30153             longitude: this.gMapContext.location.lng()
30154         };
30155     },
30156     
30157     getAddressName: function() 
30158     {
30159         return this.gMapContext.locationName;
30160     },
30161     
30162     getAddressComponents: function() 
30163     {
30164         return this.gMapContext.addressComponents;
30165     },
30166     
30167     address_component_from_google_geocode: function(address_components) 
30168     {
30169         var result = {};
30170         
30171         for (var i = 0; i < address_components.length; i++) {
30172             var component = address_components[i];
30173             if (component.types.indexOf("postal_code") >= 0) {
30174                 result.postalCode = component.short_name;
30175             } else if (component.types.indexOf("street_number") >= 0) {
30176                 result.streetNumber = component.short_name;
30177             } else if (component.types.indexOf("route") >= 0) {
30178                 result.streetName = component.short_name;
30179             } else if (component.types.indexOf("neighborhood") >= 0) {
30180                 result.city = component.short_name;
30181             } else if (component.types.indexOf("locality") >= 0) {
30182                 result.city = component.short_name;
30183             } else if (component.types.indexOf("sublocality") >= 0) {
30184                 result.district = component.short_name;
30185             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30186                 result.stateOrProvince = component.short_name;
30187             } else if (component.types.indexOf("country") >= 0) {
30188                 result.country = component.short_name;
30189             }
30190         }
30191         
30192         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30193         result.addressLine2 = "";
30194         return result;
30195     },
30196     
30197     setZoomLevel: function(zoom)
30198     {
30199         this.gMapContext.map.setZoom(zoom);
30200     },
30201     
30202     show: function()
30203     {
30204         if(!this.el){
30205             return;
30206         }
30207         
30208         this.el.show();
30209         
30210         this.resize();
30211         
30212         this.fireEvent('show', this);
30213     },
30214     
30215     hide: function()
30216     {
30217         if(!this.el){
30218             return;
30219         }
30220         
30221         this.el.hide();
30222         
30223         this.fireEvent('hide', this);
30224     }
30225     
30226 });
30227
30228 Roo.apply(Roo.bootstrap.LocationPicker, {
30229     
30230     OverlayView : function(map, options)
30231     {
30232         options = options || {};
30233         
30234         this.setMap(map);
30235     }
30236     
30237     
30238 });/**
30239  * @class Roo.bootstrap.Alert
30240  * @extends Roo.bootstrap.Component
30241  * Bootstrap Alert class - shows an alert area box
30242  * eg
30243  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30244   Enter a valid email address
30245 </div>
30246  * @licence LGPL
30247  * @cfg {String} title The title of alert
30248  * @cfg {String} html The content of alert
30249  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30250  * @cfg {String} fa font-awesomeicon
30251  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30252  * @cfg {Boolean} close true to show a x closer
30253  * 
30254  * 
30255  * @constructor
30256  * Create a new alert
30257  * @param {Object} config The config object
30258  */
30259
30260
30261 Roo.bootstrap.Alert = function(config){
30262     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30263     
30264 };
30265
30266 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30267     
30268     title: '',
30269     html: '',
30270     weight: false,
30271     fa: false,
30272     faicon: false, // BC
30273     close : false,
30274     
30275     
30276     getAutoCreate : function()
30277     {
30278         
30279         var cfg = {
30280             tag : 'div',
30281             cls : 'alert',
30282             cn : [
30283                 {
30284                     tag: 'button',
30285                     type :  "button",
30286                     cls: "close",
30287                     html : '×',
30288                     style : this.close ? '' : 'display:none'
30289                 },
30290                 {
30291                     tag : 'i',
30292                     cls : 'roo-alert-icon'
30293                     
30294                 },
30295                 {
30296                     tag : 'b',
30297                     cls : 'roo-alert-title',
30298                     html : this.title
30299                 },
30300                 {
30301                     tag : 'span',
30302                     cls : 'roo-alert-text',
30303                     html : this.html
30304                 }
30305             ]
30306         };
30307         
30308         if(this.faicon){
30309             cfg.cn[0].cls += ' fa ' + this.faicon;
30310         }
30311         if(this.fa){
30312             cfg.cn[0].cls += ' fa ' + this.fa;
30313         }
30314         
30315         if(this.weight){
30316             cfg.cls += ' alert-' + this.weight;
30317         }
30318         
30319         return cfg;
30320     },
30321     
30322     initEvents: function() 
30323     {
30324         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30325         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30326         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30327         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30328         if (this.seconds > 0) {
30329             this.hide.defer(this.seconds, this);
30330         }
30331     },
30332     /**
30333      * Set the Title Message HTML
30334      * @param {String} html
30335      */
30336     setTitle : function(str)
30337     {
30338         this.titleEl.dom.innerHTML = str;
30339     },
30340      
30341      /**
30342      * Set the Body Message HTML
30343      * @param {String} html
30344      */
30345     setHtml : function(str)
30346     {
30347         this.htmlEl.dom.innerHTML = str;
30348     },
30349     /**
30350      * Set the Weight of the alert
30351      * @param {String} (success|info|warning|danger) weight
30352      */
30353     
30354     setWeight : function(weight)
30355     {
30356         if(this.weight){
30357             this.el.removeClass('alert-' + this.weight);
30358         }
30359         
30360         this.weight = weight;
30361         
30362         this.el.addClass('alert-' + this.weight);
30363     },
30364       /**
30365      * Set the Icon of the alert
30366      * @param {String} see fontawsome names (name without the 'fa-' bit)
30367      */
30368     setIcon : function(icon)
30369     {
30370         if(this.faicon){
30371             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30372         }
30373         
30374         this.faicon = icon;
30375         
30376         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30377     },
30378     /**
30379      * Hide the Alert
30380      */
30381     hide: function() 
30382     {
30383         this.el.hide();   
30384     },
30385     /**
30386      * Show the Alert
30387      */
30388     show: function() 
30389     {  
30390         this.el.show();   
30391     }
30392     
30393 });
30394
30395  
30396 /*
30397 * Licence: LGPL
30398 */
30399
30400 /**
30401  * @class Roo.bootstrap.UploadCropbox
30402  * @extends Roo.bootstrap.Component
30403  * Bootstrap UploadCropbox class
30404  * @cfg {String} emptyText show when image has been loaded
30405  * @cfg {String} rotateNotify show when image too small to rotate
30406  * @cfg {Number} errorTimeout default 3000
30407  * @cfg {Number} minWidth default 300
30408  * @cfg {Number} minHeight default 300
30409  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30410  * @cfg {Boolean} isDocument (true|false) default false
30411  * @cfg {String} url action url
30412  * @cfg {String} paramName default 'imageUpload'
30413  * @cfg {String} method default POST
30414  * @cfg {Boolean} loadMask (true|false) default true
30415  * @cfg {Boolean} loadingText default 'Loading...'
30416  * 
30417  * @constructor
30418  * Create a new UploadCropbox
30419  * @param {Object} config The config object
30420  */
30421
30422 Roo.bootstrap.UploadCropbox = function(config){
30423     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30424     
30425     this.addEvents({
30426         /**
30427          * @event beforeselectfile
30428          * Fire before select file
30429          * @param {Roo.bootstrap.UploadCropbox} this
30430          */
30431         "beforeselectfile" : true,
30432         /**
30433          * @event initial
30434          * Fire after initEvent
30435          * @param {Roo.bootstrap.UploadCropbox} this
30436          */
30437         "initial" : true,
30438         /**
30439          * @event crop
30440          * Fire after initEvent
30441          * @param {Roo.bootstrap.UploadCropbox} this
30442          * @param {String} data
30443          */
30444         "crop" : true,
30445         /**
30446          * @event prepare
30447          * Fire when preparing the file data
30448          * @param {Roo.bootstrap.UploadCropbox} this
30449          * @param {Object} file
30450          */
30451         "prepare" : true,
30452         /**
30453          * @event exception
30454          * Fire when get exception
30455          * @param {Roo.bootstrap.UploadCropbox} this
30456          * @param {XMLHttpRequest} xhr
30457          */
30458         "exception" : true,
30459         /**
30460          * @event beforeloadcanvas
30461          * Fire before load the canvas
30462          * @param {Roo.bootstrap.UploadCropbox} this
30463          * @param {String} src
30464          */
30465         "beforeloadcanvas" : true,
30466         /**
30467          * @event trash
30468          * Fire when trash image
30469          * @param {Roo.bootstrap.UploadCropbox} this
30470          */
30471         "trash" : true,
30472         /**
30473          * @event download
30474          * Fire when download the image
30475          * @param {Roo.bootstrap.UploadCropbox} this
30476          */
30477         "download" : true,
30478         /**
30479          * @event footerbuttonclick
30480          * Fire when footerbuttonclick
30481          * @param {Roo.bootstrap.UploadCropbox} this
30482          * @param {String} type
30483          */
30484         "footerbuttonclick" : true,
30485         /**
30486          * @event resize
30487          * Fire when resize
30488          * @param {Roo.bootstrap.UploadCropbox} this
30489          */
30490         "resize" : true,
30491         /**
30492          * @event rotate
30493          * Fire when rotate the image
30494          * @param {Roo.bootstrap.UploadCropbox} this
30495          * @param {String} pos
30496          */
30497         "rotate" : true,
30498         /**
30499          * @event inspect
30500          * Fire when inspect the file
30501          * @param {Roo.bootstrap.UploadCropbox} this
30502          * @param {Object} file
30503          */
30504         "inspect" : true,
30505         /**
30506          * @event upload
30507          * Fire when xhr upload the file
30508          * @param {Roo.bootstrap.UploadCropbox} this
30509          * @param {Object} data
30510          */
30511         "upload" : true,
30512         /**
30513          * @event arrange
30514          * Fire when arrange the file data
30515          * @param {Roo.bootstrap.UploadCropbox} this
30516          * @param {Object} formData
30517          */
30518         "arrange" : true
30519     });
30520     
30521     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30522 };
30523
30524 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30525     
30526     emptyText : 'Click to upload image',
30527     rotateNotify : 'Image is too small to rotate',
30528     errorTimeout : 3000,
30529     scale : 0,
30530     baseScale : 1,
30531     rotate : 0,
30532     dragable : false,
30533     pinching : false,
30534     mouseX : 0,
30535     mouseY : 0,
30536     cropData : false,
30537     minWidth : 300,
30538     minHeight : 300,
30539     file : false,
30540     exif : {},
30541     baseRotate : 1,
30542     cropType : 'image/jpeg',
30543     buttons : false,
30544     canvasLoaded : false,
30545     isDocument : false,
30546     method : 'POST',
30547     paramName : 'imageUpload',
30548     loadMask : true,
30549     loadingText : 'Loading...',
30550     maskEl : false,
30551     
30552     getAutoCreate : function()
30553     {
30554         var cfg = {
30555             tag : 'div',
30556             cls : 'roo-upload-cropbox',
30557             cn : [
30558                 {
30559                     tag : 'input',
30560                     cls : 'roo-upload-cropbox-selector',
30561                     type : 'file'
30562                 },
30563                 {
30564                     tag : 'div',
30565                     cls : 'roo-upload-cropbox-body',
30566                     style : 'cursor:pointer',
30567                     cn : [
30568                         {
30569                             tag : 'div',
30570                             cls : 'roo-upload-cropbox-preview'
30571                         },
30572                         {
30573                             tag : 'div',
30574                             cls : 'roo-upload-cropbox-thumb'
30575                         },
30576                         {
30577                             tag : 'div',
30578                             cls : 'roo-upload-cropbox-empty-notify',
30579                             html : this.emptyText
30580                         },
30581                         {
30582                             tag : 'div',
30583                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30584                             html : this.rotateNotify
30585                         }
30586                     ]
30587                 },
30588                 {
30589                     tag : 'div',
30590                     cls : 'roo-upload-cropbox-footer',
30591                     cn : {
30592                         tag : 'div',
30593                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30594                         cn : []
30595                     }
30596                 }
30597             ]
30598         };
30599         
30600         return cfg;
30601     },
30602     
30603     onRender : function(ct, position)
30604     {
30605         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30606         
30607         if (this.buttons.length) {
30608             
30609             Roo.each(this.buttons, function(bb) {
30610                 
30611                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30612                 
30613                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30614                 
30615             }, this);
30616         }
30617         
30618         if(this.loadMask){
30619             this.maskEl = this.el;
30620         }
30621     },
30622     
30623     initEvents : function()
30624     {
30625         this.urlAPI = (window.createObjectURL && window) || 
30626                                 (window.URL && URL.revokeObjectURL && URL) || 
30627                                 (window.webkitURL && webkitURL);
30628                         
30629         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30630         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30631         
30632         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30633         this.selectorEl.hide();
30634         
30635         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30636         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30637         
30638         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30639         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30640         this.thumbEl.hide();
30641         
30642         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30643         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30644         
30645         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30646         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30647         this.errorEl.hide();
30648         
30649         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30650         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30651         this.footerEl.hide();
30652         
30653         this.setThumbBoxSize();
30654         
30655         this.bind();
30656         
30657         this.resize();
30658         
30659         this.fireEvent('initial', this);
30660     },
30661
30662     bind : function()
30663     {
30664         var _this = this;
30665         
30666         window.addEventListener("resize", function() { _this.resize(); } );
30667         
30668         this.bodyEl.on('click', this.beforeSelectFile, this);
30669         
30670         if(Roo.isTouch){
30671             this.bodyEl.on('touchstart', this.onTouchStart, this);
30672             this.bodyEl.on('touchmove', this.onTouchMove, this);
30673             this.bodyEl.on('touchend', this.onTouchEnd, this);
30674         }
30675         
30676         if(!Roo.isTouch){
30677             this.bodyEl.on('mousedown', this.onMouseDown, this);
30678             this.bodyEl.on('mousemove', this.onMouseMove, this);
30679             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30680             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30681             Roo.get(document).on('mouseup', this.onMouseUp, this);
30682         }
30683         
30684         this.selectorEl.on('change', this.onFileSelected, this);
30685     },
30686     
30687     reset : function()
30688     {    
30689         this.scale = 0;
30690         this.baseScale = 1;
30691         this.rotate = 0;
30692         this.baseRotate = 1;
30693         this.dragable = false;
30694         this.pinching = false;
30695         this.mouseX = 0;
30696         this.mouseY = 0;
30697         this.cropData = false;
30698         this.notifyEl.dom.innerHTML = this.emptyText;
30699         
30700         this.selectorEl.dom.value = '';
30701         
30702     },
30703     
30704     resize : function()
30705     {
30706         if(this.fireEvent('resize', this) != false){
30707             this.setThumbBoxPosition();
30708             this.setCanvasPosition();
30709         }
30710     },
30711     
30712     onFooterButtonClick : function(e, el, o, type)
30713     {
30714         switch (type) {
30715             case 'rotate-left' :
30716                 this.onRotateLeft(e);
30717                 break;
30718             case 'rotate-right' :
30719                 this.onRotateRight(e);
30720                 break;
30721             case 'picture' :
30722                 this.beforeSelectFile(e);
30723                 break;
30724             case 'trash' :
30725                 this.trash(e);
30726                 break;
30727             case 'crop' :
30728                 this.crop(e);
30729                 break;
30730             case 'download' :
30731                 this.download(e);
30732                 break;
30733             default :
30734                 break;
30735         }
30736         
30737         this.fireEvent('footerbuttonclick', this, type);
30738     },
30739     
30740     beforeSelectFile : function(e)
30741     {
30742         e.preventDefault();
30743         
30744         if(this.fireEvent('beforeselectfile', this) != false){
30745             this.selectorEl.dom.click();
30746         }
30747     },
30748     
30749     onFileSelected : function(e)
30750     {
30751         e.preventDefault();
30752         
30753         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30754             return;
30755         }
30756         
30757         var file = this.selectorEl.dom.files[0];
30758         
30759         if(this.fireEvent('inspect', this, file) != false){
30760             this.prepare(file);
30761         }
30762         
30763     },
30764     
30765     trash : function(e)
30766     {
30767         this.fireEvent('trash', this);
30768     },
30769     
30770     download : function(e)
30771     {
30772         this.fireEvent('download', this);
30773     },
30774     
30775     loadCanvas : function(src)
30776     {   
30777         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30778             
30779             this.reset();
30780             
30781             this.imageEl = document.createElement('img');
30782             
30783             var _this = this;
30784             
30785             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30786             
30787             this.imageEl.src = src;
30788         }
30789     },
30790     
30791     onLoadCanvas : function()
30792     {   
30793         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30794         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30795         
30796         this.bodyEl.un('click', this.beforeSelectFile, this);
30797         
30798         this.notifyEl.hide();
30799         this.thumbEl.show();
30800         this.footerEl.show();
30801         
30802         this.baseRotateLevel();
30803         
30804         if(this.isDocument){
30805             this.setThumbBoxSize();
30806         }
30807         
30808         this.setThumbBoxPosition();
30809         
30810         this.baseScaleLevel();
30811         
30812         this.draw();
30813         
30814         this.resize();
30815         
30816         this.canvasLoaded = true;
30817         
30818         if(this.loadMask){
30819             this.maskEl.unmask();
30820         }
30821         
30822     },
30823     
30824     setCanvasPosition : function()
30825     {   
30826         if(!this.canvasEl){
30827             return;
30828         }
30829         
30830         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30831         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30832         
30833         this.previewEl.setLeft(pw);
30834         this.previewEl.setTop(ph);
30835         
30836     },
30837     
30838     onMouseDown : function(e)
30839     {   
30840         e.stopEvent();
30841         
30842         this.dragable = true;
30843         this.pinching = false;
30844         
30845         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30846             this.dragable = false;
30847             return;
30848         }
30849         
30850         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30851         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30852         
30853     },
30854     
30855     onMouseMove : function(e)
30856     {   
30857         e.stopEvent();
30858         
30859         if(!this.canvasLoaded){
30860             return;
30861         }
30862         
30863         if (!this.dragable){
30864             return;
30865         }
30866         
30867         var minX = Math.ceil(this.thumbEl.getLeft(true));
30868         var minY = Math.ceil(this.thumbEl.getTop(true));
30869         
30870         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30871         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30872         
30873         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30874         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30875         
30876         x = x - this.mouseX;
30877         y = y - this.mouseY;
30878         
30879         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30880         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30881         
30882         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30883         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30884         
30885         this.previewEl.setLeft(bgX);
30886         this.previewEl.setTop(bgY);
30887         
30888         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30889         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30890     },
30891     
30892     onMouseUp : function(e)
30893     {   
30894         e.stopEvent();
30895         
30896         this.dragable = false;
30897     },
30898     
30899     onMouseWheel : function(e)
30900     {   
30901         e.stopEvent();
30902         
30903         this.startScale = this.scale;
30904         
30905         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30906         
30907         if(!this.zoomable()){
30908             this.scale = this.startScale;
30909             return;
30910         }
30911         
30912         this.draw();
30913         
30914         return;
30915     },
30916     
30917     zoomable : function()
30918     {
30919         var minScale = this.thumbEl.getWidth() / this.minWidth;
30920         
30921         if(this.minWidth < this.minHeight){
30922             minScale = this.thumbEl.getHeight() / this.minHeight;
30923         }
30924         
30925         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30926         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30927         
30928         if(
30929                 this.isDocument &&
30930                 (this.rotate == 0 || this.rotate == 180) && 
30931                 (
30932                     width > this.imageEl.OriginWidth || 
30933                     height > this.imageEl.OriginHeight ||
30934                     (width < this.minWidth && height < this.minHeight)
30935                 )
30936         ){
30937             return false;
30938         }
30939         
30940         if(
30941                 this.isDocument &&
30942                 (this.rotate == 90 || this.rotate == 270) && 
30943                 (
30944                     width > this.imageEl.OriginWidth || 
30945                     height > this.imageEl.OriginHeight ||
30946                     (width < this.minHeight && height < this.minWidth)
30947                 )
30948         ){
30949             return false;
30950         }
30951         
30952         if(
30953                 !this.isDocument &&
30954                 (this.rotate == 0 || this.rotate == 180) && 
30955                 (
30956                     width < this.minWidth || 
30957                     width > this.imageEl.OriginWidth || 
30958                     height < this.minHeight || 
30959                     height > this.imageEl.OriginHeight
30960                 )
30961         ){
30962             return false;
30963         }
30964         
30965         if(
30966                 !this.isDocument &&
30967                 (this.rotate == 90 || this.rotate == 270) && 
30968                 (
30969                     width < this.minHeight || 
30970                     width > this.imageEl.OriginWidth || 
30971                     height < this.minWidth || 
30972                     height > this.imageEl.OriginHeight
30973                 )
30974         ){
30975             return false;
30976         }
30977         
30978         return true;
30979         
30980     },
30981     
30982     onRotateLeft : function(e)
30983     {   
30984         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30985             
30986             var minScale = this.thumbEl.getWidth() / this.minWidth;
30987             
30988             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30989             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30990             
30991             this.startScale = this.scale;
30992             
30993             while (this.getScaleLevel() < minScale){
30994             
30995                 this.scale = this.scale + 1;
30996                 
30997                 if(!this.zoomable()){
30998                     break;
30999                 }
31000                 
31001                 if(
31002                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31003                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31004                 ){
31005                     continue;
31006                 }
31007                 
31008                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31009
31010                 this.draw();
31011                 
31012                 return;
31013             }
31014             
31015             this.scale = this.startScale;
31016             
31017             this.onRotateFail();
31018             
31019             return false;
31020         }
31021         
31022         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31023
31024         if(this.isDocument){
31025             this.setThumbBoxSize();
31026             this.setThumbBoxPosition();
31027             this.setCanvasPosition();
31028         }
31029         
31030         this.draw();
31031         
31032         this.fireEvent('rotate', this, 'left');
31033         
31034     },
31035     
31036     onRotateRight : function(e)
31037     {
31038         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31039             
31040             var minScale = this.thumbEl.getWidth() / this.minWidth;
31041         
31042             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31043             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31044             
31045             this.startScale = this.scale;
31046             
31047             while (this.getScaleLevel() < minScale){
31048             
31049                 this.scale = this.scale + 1;
31050                 
31051                 if(!this.zoomable()){
31052                     break;
31053                 }
31054                 
31055                 if(
31056                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31057                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31058                 ){
31059                     continue;
31060                 }
31061                 
31062                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31063
31064                 this.draw();
31065                 
31066                 return;
31067             }
31068             
31069             this.scale = this.startScale;
31070             
31071             this.onRotateFail();
31072             
31073             return false;
31074         }
31075         
31076         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31077
31078         if(this.isDocument){
31079             this.setThumbBoxSize();
31080             this.setThumbBoxPosition();
31081             this.setCanvasPosition();
31082         }
31083         
31084         this.draw();
31085         
31086         this.fireEvent('rotate', this, 'right');
31087     },
31088     
31089     onRotateFail : function()
31090     {
31091         this.errorEl.show(true);
31092         
31093         var _this = this;
31094         
31095         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31096     },
31097     
31098     draw : function()
31099     {
31100         this.previewEl.dom.innerHTML = '';
31101         
31102         var canvasEl = document.createElement("canvas");
31103         
31104         var contextEl = canvasEl.getContext("2d");
31105         
31106         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31107         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31108         var center = this.imageEl.OriginWidth / 2;
31109         
31110         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31111             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31112             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31113             center = this.imageEl.OriginHeight / 2;
31114         }
31115         
31116         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31117         
31118         contextEl.translate(center, center);
31119         contextEl.rotate(this.rotate * Math.PI / 180);
31120
31121         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31122         
31123         this.canvasEl = document.createElement("canvas");
31124         
31125         this.contextEl = this.canvasEl.getContext("2d");
31126         
31127         switch (this.rotate) {
31128             case 0 :
31129                 
31130                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31131                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31132                 
31133                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31134                 
31135                 break;
31136             case 90 : 
31137                 
31138                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31139                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31140                 
31141                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31142                     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);
31143                     break;
31144                 }
31145                 
31146                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31147                 
31148                 break;
31149             case 180 :
31150                 
31151                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31152                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31153                 
31154                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31155                     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);
31156                     break;
31157                 }
31158                 
31159                 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);
31160                 
31161                 break;
31162             case 270 :
31163                 
31164                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31165                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31166         
31167                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31168                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31169                     break;
31170                 }
31171                 
31172                 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);
31173                 
31174                 break;
31175             default : 
31176                 break;
31177         }
31178         
31179         this.previewEl.appendChild(this.canvasEl);
31180         
31181         this.setCanvasPosition();
31182     },
31183     
31184     crop : function()
31185     {
31186         if(!this.canvasLoaded){
31187             return;
31188         }
31189         
31190         var imageCanvas = document.createElement("canvas");
31191         
31192         var imageContext = imageCanvas.getContext("2d");
31193         
31194         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31195         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31196         
31197         var center = imageCanvas.width / 2;
31198         
31199         imageContext.translate(center, center);
31200         
31201         imageContext.rotate(this.rotate * Math.PI / 180);
31202         
31203         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31204         
31205         var canvas = document.createElement("canvas");
31206         
31207         var context = canvas.getContext("2d");
31208                 
31209         canvas.width = this.minWidth;
31210         canvas.height = this.minHeight;
31211
31212         switch (this.rotate) {
31213             case 0 :
31214                 
31215                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31216                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31217                 
31218                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31219                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31220                 
31221                 var targetWidth = this.minWidth - 2 * x;
31222                 var targetHeight = this.minHeight - 2 * y;
31223                 
31224                 var scale = 1;
31225                 
31226                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31227                     scale = targetWidth / width;
31228                 }
31229                 
31230                 if(x > 0 && y == 0){
31231                     scale = targetHeight / height;
31232                 }
31233                 
31234                 if(x > 0 && y > 0){
31235                     scale = targetWidth / width;
31236                     
31237                     if(width < height){
31238                         scale = targetHeight / height;
31239                     }
31240                 }
31241                 
31242                 context.scale(scale, scale);
31243                 
31244                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31245                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31246
31247                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31248                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31249
31250                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31251                 
31252                 break;
31253             case 90 : 
31254                 
31255                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31256                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31257                 
31258                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31259                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31260                 
31261                 var targetWidth = this.minWidth - 2 * x;
31262                 var targetHeight = this.minHeight - 2 * y;
31263                 
31264                 var scale = 1;
31265                 
31266                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31267                     scale = targetWidth / width;
31268                 }
31269                 
31270                 if(x > 0 && y == 0){
31271                     scale = targetHeight / height;
31272                 }
31273                 
31274                 if(x > 0 && y > 0){
31275                     scale = targetWidth / width;
31276                     
31277                     if(width < height){
31278                         scale = targetHeight / height;
31279                     }
31280                 }
31281                 
31282                 context.scale(scale, scale);
31283                 
31284                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31285                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31286
31287                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31288                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31289                 
31290                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31291                 
31292                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31293                 
31294                 break;
31295             case 180 :
31296                 
31297                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31298                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31299                 
31300                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31301                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31302                 
31303                 var targetWidth = this.minWidth - 2 * x;
31304                 var targetHeight = this.minHeight - 2 * y;
31305                 
31306                 var scale = 1;
31307                 
31308                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31309                     scale = targetWidth / width;
31310                 }
31311                 
31312                 if(x > 0 && y == 0){
31313                     scale = targetHeight / height;
31314                 }
31315                 
31316                 if(x > 0 && y > 0){
31317                     scale = targetWidth / width;
31318                     
31319                     if(width < height){
31320                         scale = targetHeight / height;
31321                     }
31322                 }
31323                 
31324                 context.scale(scale, scale);
31325                 
31326                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31327                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31328
31329                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31330                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31331
31332                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31333                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31334                 
31335                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31336                 
31337                 break;
31338             case 270 :
31339                 
31340                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31341                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31342                 
31343                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31344                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31345                 
31346                 var targetWidth = this.minWidth - 2 * x;
31347                 var targetHeight = this.minHeight - 2 * y;
31348                 
31349                 var scale = 1;
31350                 
31351                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31352                     scale = targetWidth / width;
31353                 }
31354                 
31355                 if(x > 0 && y == 0){
31356                     scale = targetHeight / height;
31357                 }
31358                 
31359                 if(x > 0 && y > 0){
31360                     scale = targetWidth / width;
31361                     
31362                     if(width < height){
31363                         scale = targetHeight / height;
31364                     }
31365                 }
31366                 
31367                 context.scale(scale, scale);
31368                 
31369                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31370                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31371
31372                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31373                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31374                 
31375                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31376                 
31377                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31378                 
31379                 break;
31380             default : 
31381                 break;
31382         }
31383         
31384         this.cropData = canvas.toDataURL(this.cropType);
31385         
31386         if(this.fireEvent('crop', this, this.cropData) !== false){
31387             this.process(this.file, this.cropData);
31388         }
31389         
31390         return;
31391         
31392     },
31393     
31394     setThumbBoxSize : function()
31395     {
31396         var width, height;
31397         
31398         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31399             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31400             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31401             
31402             this.minWidth = width;
31403             this.minHeight = height;
31404             
31405             if(this.rotate == 90 || this.rotate == 270){
31406                 this.minWidth = height;
31407                 this.minHeight = width;
31408             }
31409         }
31410         
31411         height = 300;
31412         width = Math.ceil(this.minWidth * height / this.minHeight);
31413         
31414         if(this.minWidth > this.minHeight){
31415             width = 300;
31416             height = Math.ceil(this.minHeight * width / this.minWidth);
31417         }
31418         
31419         this.thumbEl.setStyle({
31420             width : width + 'px',
31421             height : height + 'px'
31422         });
31423
31424         return;
31425             
31426     },
31427     
31428     setThumbBoxPosition : function()
31429     {
31430         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31431         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31432         
31433         this.thumbEl.setLeft(x);
31434         this.thumbEl.setTop(y);
31435         
31436     },
31437     
31438     baseRotateLevel : function()
31439     {
31440         this.baseRotate = 1;
31441         
31442         if(
31443                 typeof(this.exif) != 'undefined' &&
31444                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31445                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31446         ){
31447             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31448         }
31449         
31450         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31451         
31452     },
31453     
31454     baseScaleLevel : function()
31455     {
31456         var width, height;
31457         
31458         if(this.isDocument){
31459             
31460             if(this.baseRotate == 6 || this.baseRotate == 8){
31461             
31462                 height = this.thumbEl.getHeight();
31463                 this.baseScale = height / this.imageEl.OriginWidth;
31464
31465                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31466                     width = this.thumbEl.getWidth();
31467                     this.baseScale = width / this.imageEl.OriginHeight;
31468                 }
31469
31470                 return;
31471             }
31472
31473             height = this.thumbEl.getHeight();
31474             this.baseScale = height / this.imageEl.OriginHeight;
31475
31476             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31477                 width = this.thumbEl.getWidth();
31478                 this.baseScale = width / this.imageEl.OriginWidth;
31479             }
31480
31481             return;
31482         }
31483         
31484         if(this.baseRotate == 6 || this.baseRotate == 8){
31485             
31486             width = this.thumbEl.getHeight();
31487             this.baseScale = width / this.imageEl.OriginHeight;
31488             
31489             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31490                 height = this.thumbEl.getWidth();
31491                 this.baseScale = height / this.imageEl.OriginHeight;
31492             }
31493             
31494             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31495                 height = this.thumbEl.getWidth();
31496                 this.baseScale = height / this.imageEl.OriginHeight;
31497                 
31498                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31499                     width = this.thumbEl.getHeight();
31500                     this.baseScale = width / this.imageEl.OriginWidth;
31501                 }
31502             }
31503             
31504             return;
31505         }
31506         
31507         width = this.thumbEl.getWidth();
31508         this.baseScale = width / this.imageEl.OriginWidth;
31509         
31510         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31511             height = this.thumbEl.getHeight();
31512             this.baseScale = height / this.imageEl.OriginHeight;
31513         }
31514         
31515         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31516             
31517             height = this.thumbEl.getHeight();
31518             this.baseScale = height / this.imageEl.OriginHeight;
31519             
31520             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31521                 width = this.thumbEl.getWidth();
31522                 this.baseScale = width / this.imageEl.OriginWidth;
31523             }
31524             
31525         }
31526         
31527         return;
31528     },
31529     
31530     getScaleLevel : function()
31531     {
31532         return this.baseScale * Math.pow(1.1, this.scale);
31533     },
31534     
31535     onTouchStart : function(e)
31536     {
31537         if(!this.canvasLoaded){
31538             this.beforeSelectFile(e);
31539             return;
31540         }
31541         
31542         var touches = e.browserEvent.touches;
31543         
31544         if(!touches){
31545             return;
31546         }
31547         
31548         if(touches.length == 1){
31549             this.onMouseDown(e);
31550             return;
31551         }
31552         
31553         if(touches.length != 2){
31554             return;
31555         }
31556         
31557         var coords = [];
31558         
31559         for(var i = 0, finger; finger = touches[i]; i++){
31560             coords.push(finger.pageX, finger.pageY);
31561         }
31562         
31563         var x = Math.pow(coords[0] - coords[2], 2);
31564         var y = Math.pow(coords[1] - coords[3], 2);
31565         
31566         this.startDistance = Math.sqrt(x + y);
31567         
31568         this.startScale = this.scale;
31569         
31570         this.pinching = true;
31571         this.dragable = false;
31572         
31573     },
31574     
31575     onTouchMove : function(e)
31576     {
31577         if(!this.pinching && !this.dragable){
31578             return;
31579         }
31580         
31581         var touches = e.browserEvent.touches;
31582         
31583         if(!touches){
31584             return;
31585         }
31586         
31587         if(this.dragable){
31588             this.onMouseMove(e);
31589             return;
31590         }
31591         
31592         var coords = [];
31593         
31594         for(var i = 0, finger; finger = touches[i]; i++){
31595             coords.push(finger.pageX, finger.pageY);
31596         }
31597         
31598         var x = Math.pow(coords[0] - coords[2], 2);
31599         var y = Math.pow(coords[1] - coords[3], 2);
31600         
31601         this.endDistance = Math.sqrt(x + y);
31602         
31603         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31604         
31605         if(!this.zoomable()){
31606             this.scale = this.startScale;
31607             return;
31608         }
31609         
31610         this.draw();
31611         
31612     },
31613     
31614     onTouchEnd : function(e)
31615     {
31616         this.pinching = false;
31617         this.dragable = false;
31618         
31619     },
31620     
31621     process : function(file, crop)
31622     {
31623         if(this.loadMask){
31624             this.maskEl.mask(this.loadingText);
31625         }
31626         
31627         this.xhr = new XMLHttpRequest();
31628         
31629         file.xhr = this.xhr;
31630
31631         this.xhr.open(this.method, this.url, true);
31632         
31633         var headers = {
31634             "Accept": "application/json",
31635             "Cache-Control": "no-cache",
31636             "X-Requested-With": "XMLHttpRequest"
31637         };
31638         
31639         for (var headerName in headers) {
31640             var headerValue = headers[headerName];
31641             if (headerValue) {
31642                 this.xhr.setRequestHeader(headerName, headerValue);
31643             }
31644         }
31645         
31646         var _this = this;
31647         
31648         this.xhr.onload = function()
31649         {
31650             _this.xhrOnLoad(_this.xhr);
31651         }
31652         
31653         this.xhr.onerror = function()
31654         {
31655             _this.xhrOnError(_this.xhr);
31656         }
31657         
31658         var formData = new FormData();
31659
31660         formData.append('returnHTML', 'NO');
31661         
31662         if(crop){
31663             formData.append('crop', crop);
31664         }
31665         
31666         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31667             formData.append(this.paramName, file, file.name);
31668         }
31669         
31670         if(typeof(file.filename) != 'undefined'){
31671             formData.append('filename', file.filename);
31672         }
31673         
31674         if(typeof(file.mimetype) != 'undefined'){
31675             formData.append('mimetype', file.mimetype);
31676         }
31677         
31678         if(this.fireEvent('arrange', this, formData) != false){
31679             this.xhr.send(formData);
31680         };
31681     },
31682     
31683     xhrOnLoad : function(xhr)
31684     {
31685         if(this.loadMask){
31686             this.maskEl.unmask();
31687         }
31688         
31689         if (xhr.readyState !== 4) {
31690             this.fireEvent('exception', this, xhr);
31691             return;
31692         }
31693
31694         var response = Roo.decode(xhr.responseText);
31695         
31696         if(!response.success){
31697             this.fireEvent('exception', this, xhr);
31698             return;
31699         }
31700         
31701         var response = Roo.decode(xhr.responseText);
31702         
31703         this.fireEvent('upload', this, response);
31704         
31705     },
31706     
31707     xhrOnError : function()
31708     {
31709         if(this.loadMask){
31710             this.maskEl.unmask();
31711         }
31712         
31713         Roo.log('xhr on error');
31714         
31715         var response = Roo.decode(xhr.responseText);
31716           
31717         Roo.log(response);
31718         
31719     },
31720     
31721     prepare : function(file)
31722     {   
31723         if(this.loadMask){
31724             this.maskEl.mask(this.loadingText);
31725         }
31726         
31727         this.file = false;
31728         this.exif = {};
31729         
31730         if(typeof(file) === 'string'){
31731             this.loadCanvas(file);
31732             return;
31733         }
31734         
31735         if(!file || !this.urlAPI){
31736             return;
31737         }
31738         
31739         this.file = file;
31740         this.cropType = file.type;
31741         
31742         var _this = this;
31743         
31744         if(this.fireEvent('prepare', this, this.file) != false){
31745             
31746             var reader = new FileReader();
31747             
31748             reader.onload = function (e) {
31749                 if (e.target.error) {
31750                     Roo.log(e.target.error);
31751                     return;
31752                 }
31753                 
31754                 var buffer = e.target.result,
31755                     dataView = new DataView(buffer),
31756                     offset = 2,
31757                     maxOffset = dataView.byteLength - 4,
31758                     markerBytes,
31759                     markerLength;
31760                 
31761                 if (dataView.getUint16(0) === 0xffd8) {
31762                     while (offset < maxOffset) {
31763                         markerBytes = dataView.getUint16(offset);
31764                         
31765                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31766                             markerLength = dataView.getUint16(offset + 2) + 2;
31767                             if (offset + markerLength > dataView.byteLength) {
31768                                 Roo.log('Invalid meta data: Invalid segment size.');
31769                                 break;
31770                             }
31771                             
31772                             if(markerBytes == 0xffe1){
31773                                 _this.parseExifData(
31774                                     dataView,
31775                                     offset,
31776                                     markerLength
31777                                 );
31778                             }
31779                             
31780                             offset += markerLength;
31781                             
31782                             continue;
31783                         }
31784                         
31785                         break;
31786                     }
31787                     
31788                 }
31789                 
31790                 var url = _this.urlAPI.createObjectURL(_this.file);
31791                 
31792                 _this.loadCanvas(url);
31793                 
31794                 return;
31795             }
31796             
31797             reader.readAsArrayBuffer(this.file);
31798             
31799         }
31800         
31801     },
31802     
31803     parseExifData : function(dataView, offset, length)
31804     {
31805         var tiffOffset = offset + 10,
31806             littleEndian,
31807             dirOffset;
31808     
31809         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31810             // No Exif data, might be XMP data instead
31811             return;
31812         }
31813         
31814         // Check for the ASCII code for "Exif" (0x45786966):
31815         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31816             // No Exif data, might be XMP data instead
31817             return;
31818         }
31819         if (tiffOffset + 8 > dataView.byteLength) {
31820             Roo.log('Invalid Exif data: Invalid segment size.');
31821             return;
31822         }
31823         // Check for the two null bytes:
31824         if (dataView.getUint16(offset + 8) !== 0x0000) {
31825             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31826             return;
31827         }
31828         // Check the byte alignment:
31829         switch (dataView.getUint16(tiffOffset)) {
31830         case 0x4949:
31831             littleEndian = true;
31832             break;
31833         case 0x4D4D:
31834             littleEndian = false;
31835             break;
31836         default:
31837             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31838             return;
31839         }
31840         // Check for the TIFF tag marker (0x002A):
31841         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31842             Roo.log('Invalid Exif data: Missing TIFF marker.');
31843             return;
31844         }
31845         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31846         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31847         
31848         this.parseExifTags(
31849             dataView,
31850             tiffOffset,
31851             tiffOffset + dirOffset,
31852             littleEndian
31853         );
31854     },
31855     
31856     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31857     {
31858         var tagsNumber,
31859             dirEndOffset,
31860             i;
31861         if (dirOffset + 6 > dataView.byteLength) {
31862             Roo.log('Invalid Exif data: Invalid directory offset.');
31863             return;
31864         }
31865         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31866         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31867         if (dirEndOffset + 4 > dataView.byteLength) {
31868             Roo.log('Invalid Exif data: Invalid directory size.');
31869             return;
31870         }
31871         for (i = 0; i < tagsNumber; i += 1) {
31872             this.parseExifTag(
31873                 dataView,
31874                 tiffOffset,
31875                 dirOffset + 2 + 12 * i, // tag offset
31876                 littleEndian
31877             );
31878         }
31879         // Return the offset to the next directory:
31880         return dataView.getUint32(dirEndOffset, littleEndian);
31881     },
31882     
31883     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31884     {
31885         var tag = dataView.getUint16(offset, littleEndian);
31886         
31887         this.exif[tag] = this.getExifValue(
31888             dataView,
31889             tiffOffset,
31890             offset,
31891             dataView.getUint16(offset + 2, littleEndian), // tag type
31892             dataView.getUint32(offset + 4, littleEndian), // tag length
31893             littleEndian
31894         );
31895     },
31896     
31897     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31898     {
31899         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31900             tagSize,
31901             dataOffset,
31902             values,
31903             i,
31904             str,
31905             c;
31906     
31907         if (!tagType) {
31908             Roo.log('Invalid Exif data: Invalid tag type.');
31909             return;
31910         }
31911         
31912         tagSize = tagType.size * length;
31913         // Determine if the value is contained in the dataOffset bytes,
31914         // or if the value at the dataOffset is a pointer to the actual data:
31915         dataOffset = tagSize > 4 ?
31916                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31917         if (dataOffset + tagSize > dataView.byteLength) {
31918             Roo.log('Invalid Exif data: Invalid data offset.');
31919             return;
31920         }
31921         if (length === 1) {
31922             return tagType.getValue(dataView, dataOffset, littleEndian);
31923         }
31924         values = [];
31925         for (i = 0; i < length; i += 1) {
31926             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31927         }
31928         
31929         if (tagType.ascii) {
31930             str = '';
31931             // Concatenate the chars:
31932             for (i = 0; i < values.length; i += 1) {
31933                 c = values[i];
31934                 // Ignore the terminating NULL byte(s):
31935                 if (c === '\u0000') {
31936                     break;
31937                 }
31938                 str += c;
31939             }
31940             return str;
31941         }
31942         return values;
31943     }
31944     
31945 });
31946
31947 Roo.apply(Roo.bootstrap.UploadCropbox, {
31948     tags : {
31949         'Orientation': 0x0112
31950     },
31951     
31952     Orientation: {
31953             1: 0, //'top-left',
31954 //            2: 'top-right',
31955             3: 180, //'bottom-right',
31956 //            4: 'bottom-left',
31957 //            5: 'left-top',
31958             6: 90, //'right-top',
31959 //            7: 'right-bottom',
31960             8: 270 //'left-bottom'
31961     },
31962     
31963     exifTagTypes : {
31964         // byte, 8-bit unsigned int:
31965         1: {
31966             getValue: function (dataView, dataOffset) {
31967                 return dataView.getUint8(dataOffset);
31968             },
31969             size: 1
31970         },
31971         // ascii, 8-bit byte:
31972         2: {
31973             getValue: function (dataView, dataOffset) {
31974                 return String.fromCharCode(dataView.getUint8(dataOffset));
31975             },
31976             size: 1,
31977             ascii: true
31978         },
31979         // short, 16 bit int:
31980         3: {
31981             getValue: function (dataView, dataOffset, littleEndian) {
31982                 return dataView.getUint16(dataOffset, littleEndian);
31983             },
31984             size: 2
31985         },
31986         // long, 32 bit int:
31987         4: {
31988             getValue: function (dataView, dataOffset, littleEndian) {
31989                 return dataView.getUint32(dataOffset, littleEndian);
31990             },
31991             size: 4
31992         },
31993         // rational = two long values, first is numerator, second is denominator:
31994         5: {
31995             getValue: function (dataView, dataOffset, littleEndian) {
31996                 return dataView.getUint32(dataOffset, littleEndian) /
31997                     dataView.getUint32(dataOffset + 4, littleEndian);
31998             },
31999             size: 8
32000         },
32001         // slong, 32 bit signed int:
32002         9: {
32003             getValue: function (dataView, dataOffset, littleEndian) {
32004                 return dataView.getInt32(dataOffset, littleEndian);
32005             },
32006             size: 4
32007         },
32008         // srational, two slongs, first is numerator, second is denominator:
32009         10: {
32010             getValue: function (dataView, dataOffset, littleEndian) {
32011                 return dataView.getInt32(dataOffset, littleEndian) /
32012                     dataView.getInt32(dataOffset + 4, littleEndian);
32013             },
32014             size: 8
32015         }
32016     },
32017     
32018     footer : {
32019         STANDARD : [
32020             {
32021                 tag : 'div',
32022                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32023                 action : 'rotate-left',
32024                 cn : [
32025                     {
32026                         tag : 'button',
32027                         cls : 'btn btn-default',
32028                         html : '<i class="fa fa-undo"></i>'
32029                     }
32030                 ]
32031             },
32032             {
32033                 tag : 'div',
32034                 cls : 'btn-group roo-upload-cropbox-picture',
32035                 action : 'picture',
32036                 cn : [
32037                     {
32038                         tag : 'button',
32039                         cls : 'btn btn-default',
32040                         html : '<i class="fa fa-picture-o"></i>'
32041                     }
32042                 ]
32043             },
32044             {
32045                 tag : 'div',
32046                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32047                 action : 'rotate-right',
32048                 cn : [
32049                     {
32050                         tag : 'button',
32051                         cls : 'btn btn-default',
32052                         html : '<i class="fa fa-repeat"></i>'
32053                     }
32054                 ]
32055             }
32056         ],
32057         DOCUMENT : [
32058             {
32059                 tag : 'div',
32060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32061                 action : 'rotate-left',
32062                 cn : [
32063                     {
32064                         tag : 'button',
32065                         cls : 'btn btn-default',
32066                         html : '<i class="fa fa-undo"></i>'
32067                     }
32068                 ]
32069             },
32070             {
32071                 tag : 'div',
32072                 cls : 'btn-group roo-upload-cropbox-download',
32073                 action : 'download',
32074                 cn : [
32075                     {
32076                         tag : 'button',
32077                         cls : 'btn btn-default',
32078                         html : '<i class="fa fa-download"></i>'
32079                     }
32080                 ]
32081             },
32082             {
32083                 tag : 'div',
32084                 cls : 'btn-group roo-upload-cropbox-crop',
32085                 action : 'crop',
32086                 cn : [
32087                     {
32088                         tag : 'button',
32089                         cls : 'btn btn-default',
32090                         html : '<i class="fa fa-crop"></i>'
32091                     }
32092                 ]
32093             },
32094             {
32095                 tag : 'div',
32096                 cls : 'btn-group roo-upload-cropbox-trash',
32097                 action : 'trash',
32098                 cn : [
32099                     {
32100                         tag : 'button',
32101                         cls : 'btn btn-default',
32102                         html : '<i class="fa fa-trash"></i>'
32103                     }
32104                 ]
32105             },
32106             {
32107                 tag : 'div',
32108                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32109                 action : 'rotate-right',
32110                 cn : [
32111                     {
32112                         tag : 'button',
32113                         cls : 'btn btn-default',
32114                         html : '<i class="fa fa-repeat"></i>'
32115                     }
32116                 ]
32117             }
32118         ],
32119         ROTATOR : [
32120             {
32121                 tag : 'div',
32122                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32123                 action : 'rotate-left',
32124                 cn : [
32125                     {
32126                         tag : 'button',
32127                         cls : 'btn btn-default',
32128                         html : '<i class="fa fa-undo"></i>'
32129                     }
32130                 ]
32131             },
32132             {
32133                 tag : 'div',
32134                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32135                 action : 'rotate-right',
32136                 cn : [
32137                     {
32138                         tag : 'button',
32139                         cls : 'btn btn-default',
32140                         html : '<i class="fa fa-repeat"></i>'
32141                     }
32142                 ]
32143             }
32144         ]
32145     }
32146 });
32147
32148 /*
32149 * Licence: LGPL
32150 */
32151
32152 /**
32153  * @class Roo.bootstrap.DocumentManager
32154  * @extends Roo.bootstrap.Component
32155  * Bootstrap DocumentManager class
32156  * @cfg {String} paramName default 'imageUpload'
32157  * @cfg {String} toolTipName default 'filename'
32158  * @cfg {String} method default POST
32159  * @cfg {String} url action url
32160  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32161  * @cfg {Boolean} multiple multiple upload default true
32162  * @cfg {Number} thumbSize default 300
32163  * @cfg {String} fieldLabel
32164  * @cfg {Number} labelWidth default 4
32165  * @cfg {String} labelAlign (left|top) default left
32166  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32167 * @cfg {Number} labellg set the width of label (1-12)
32168  * @cfg {Number} labelmd set the width of label (1-12)
32169  * @cfg {Number} labelsm set the width of label (1-12)
32170  * @cfg {Number} labelxs set the width of label (1-12)
32171  * 
32172  * @constructor
32173  * Create a new DocumentManager
32174  * @param {Object} config The config object
32175  */
32176
32177 Roo.bootstrap.DocumentManager = function(config){
32178     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32179     
32180     this.files = [];
32181     this.delegates = [];
32182     
32183     this.addEvents({
32184         /**
32185          * @event initial
32186          * Fire when initial the DocumentManager
32187          * @param {Roo.bootstrap.DocumentManager} this
32188          */
32189         "initial" : true,
32190         /**
32191          * @event inspect
32192          * inspect selected file
32193          * @param {Roo.bootstrap.DocumentManager} this
32194          * @param {File} file
32195          */
32196         "inspect" : true,
32197         /**
32198          * @event exception
32199          * Fire when xhr load exception
32200          * @param {Roo.bootstrap.DocumentManager} this
32201          * @param {XMLHttpRequest} xhr
32202          */
32203         "exception" : true,
32204         /**
32205          * @event afterupload
32206          * Fire when xhr load exception
32207          * @param {Roo.bootstrap.DocumentManager} this
32208          * @param {XMLHttpRequest} xhr
32209          */
32210         "afterupload" : true,
32211         /**
32212          * @event prepare
32213          * prepare the form data
32214          * @param {Roo.bootstrap.DocumentManager} this
32215          * @param {Object} formData
32216          */
32217         "prepare" : true,
32218         /**
32219          * @event remove
32220          * Fire when remove the file
32221          * @param {Roo.bootstrap.DocumentManager} this
32222          * @param {Object} file
32223          */
32224         "remove" : true,
32225         /**
32226          * @event refresh
32227          * Fire after refresh the file
32228          * @param {Roo.bootstrap.DocumentManager} this
32229          */
32230         "refresh" : true,
32231         /**
32232          * @event click
32233          * Fire after click the image
32234          * @param {Roo.bootstrap.DocumentManager} this
32235          * @param {Object} file
32236          */
32237         "click" : true,
32238         /**
32239          * @event edit
32240          * Fire when upload a image and editable set to true
32241          * @param {Roo.bootstrap.DocumentManager} this
32242          * @param {Object} file
32243          */
32244         "edit" : true,
32245         /**
32246          * @event beforeselectfile
32247          * Fire before select file
32248          * @param {Roo.bootstrap.DocumentManager} this
32249          */
32250         "beforeselectfile" : true,
32251         /**
32252          * @event process
32253          * Fire before process file
32254          * @param {Roo.bootstrap.DocumentManager} this
32255          * @param {Object} file
32256          */
32257         "process" : true,
32258         /**
32259          * @event previewrendered
32260          * Fire when preview rendered
32261          * @param {Roo.bootstrap.DocumentManager} this
32262          * @param {Object} file
32263          */
32264         "previewrendered" : true,
32265         /**
32266          */
32267         "previewResize" : true
32268         
32269     });
32270 };
32271
32272 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32273     
32274     boxes : 0,
32275     inputName : '',
32276     thumbSize : 300,
32277     multiple : true,
32278     files : false,
32279     method : 'POST',
32280     url : '',
32281     paramName : 'imageUpload',
32282     toolTipName : 'filename',
32283     fieldLabel : '',
32284     labelWidth : 4,
32285     labelAlign : 'left',
32286     editable : true,
32287     delegates : false,
32288     xhr : false, 
32289     
32290     labellg : 0,
32291     labelmd : 0,
32292     labelsm : 0,
32293     labelxs : 0,
32294     
32295     getAutoCreate : function()
32296     {   
32297         var managerWidget = {
32298             tag : 'div',
32299             cls : 'roo-document-manager',
32300             cn : [
32301                 {
32302                     tag : 'input',
32303                     cls : 'roo-document-manager-selector',
32304                     type : 'file'
32305                 },
32306                 {
32307                     tag : 'div',
32308                     cls : 'roo-document-manager-uploader',
32309                     cn : [
32310                         {
32311                             tag : 'div',
32312                             cls : 'roo-document-manager-upload-btn',
32313                             html : '<i class="fa fa-plus"></i>'
32314                         }
32315                     ]
32316                     
32317                 }
32318             ]
32319         };
32320         
32321         var content = [
32322             {
32323                 tag : 'div',
32324                 cls : 'column col-md-12',
32325                 cn : managerWidget
32326             }
32327         ];
32328         
32329         if(this.fieldLabel.length){
32330             
32331             content = [
32332                 {
32333                     tag : 'div',
32334                     cls : 'column col-md-12',
32335                     html : this.fieldLabel
32336                 },
32337                 {
32338                     tag : 'div',
32339                     cls : 'column col-md-12',
32340                     cn : managerWidget
32341                 }
32342             ];
32343
32344             if(this.labelAlign == 'left'){
32345                 content = [
32346                     {
32347                         tag : 'div',
32348                         cls : 'column',
32349                         html : this.fieldLabel
32350                     },
32351                     {
32352                         tag : 'div',
32353                         cls : 'column',
32354                         cn : managerWidget
32355                     }
32356                 ];
32357                 
32358                 if(this.labelWidth > 12){
32359                     content[0].style = "width: " + this.labelWidth + 'px';
32360                 }
32361
32362                 if(this.labelWidth < 13 && this.labelmd == 0){
32363                     this.labelmd = this.labelWidth;
32364                 }
32365
32366                 if(this.labellg > 0){
32367                     content[0].cls += ' col-lg-' + this.labellg;
32368                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32369                 }
32370
32371                 if(this.labelmd > 0){
32372                     content[0].cls += ' col-md-' + this.labelmd;
32373                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32374                 }
32375
32376                 if(this.labelsm > 0){
32377                     content[0].cls += ' col-sm-' + this.labelsm;
32378                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32379                 }
32380
32381                 if(this.labelxs > 0){
32382                     content[0].cls += ' col-xs-' + this.labelxs;
32383                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32384                 }
32385                 
32386             }
32387         }
32388         
32389         var cfg = {
32390             tag : 'div',
32391             cls : 'row clearfix',
32392             cn : content
32393         };
32394         
32395         return cfg;
32396         
32397     },
32398     
32399     initEvents : function()
32400     {
32401         this.managerEl = this.el.select('.roo-document-manager', true).first();
32402         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32403         
32404         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32405         this.selectorEl.hide();
32406         
32407         if(this.multiple){
32408             this.selectorEl.attr('multiple', 'multiple');
32409         }
32410         
32411         this.selectorEl.on('change', this.onFileSelected, this);
32412         
32413         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32414         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32415         
32416         this.uploader.on('click', this.onUploaderClick, this);
32417         
32418         this.renderProgressDialog();
32419         
32420         var _this = this;
32421         
32422         window.addEventListener("resize", function() { _this.refresh(); } );
32423         
32424         this.fireEvent('initial', this);
32425     },
32426     
32427     renderProgressDialog : function()
32428     {
32429         var _this = this;
32430         
32431         this.progressDialog = new Roo.bootstrap.Modal({
32432             cls : 'roo-document-manager-progress-dialog',
32433             allow_close : false,
32434             animate : false,
32435             title : '',
32436             buttons : [
32437                 {
32438                     name  :'cancel',
32439                     weight : 'danger',
32440                     html : 'Cancel'
32441                 }
32442             ], 
32443             listeners : { 
32444                 btnclick : function() {
32445                     _this.uploadCancel();
32446                     this.hide();
32447                 }
32448             }
32449         });
32450          
32451         this.progressDialog.render(Roo.get(document.body));
32452          
32453         this.progress = new Roo.bootstrap.Progress({
32454             cls : 'roo-document-manager-progress',
32455             active : true,
32456             striped : true
32457         });
32458         
32459         this.progress.render(this.progressDialog.getChildContainer());
32460         
32461         this.progressBar = new Roo.bootstrap.ProgressBar({
32462             cls : 'roo-document-manager-progress-bar',
32463             aria_valuenow : 0,
32464             aria_valuemin : 0,
32465             aria_valuemax : 12,
32466             panel : 'success'
32467         });
32468         
32469         this.progressBar.render(this.progress.getChildContainer());
32470     },
32471     
32472     onUploaderClick : function(e)
32473     {
32474         e.preventDefault();
32475      
32476         if(this.fireEvent('beforeselectfile', this) != false){
32477             this.selectorEl.dom.click();
32478         }
32479         
32480     },
32481     
32482     onFileSelected : function(e)
32483     {
32484         e.preventDefault();
32485         
32486         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32487             return;
32488         }
32489         
32490         Roo.each(this.selectorEl.dom.files, function(file){
32491             if(this.fireEvent('inspect', this, file) != false){
32492                 this.files.push(file);
32493             }
32494         }, this);
32495         
32496         this.queue();
32497         
32498     },
32499     
32500     queue : function()
32501     {
32502         this.selectorEl.dom.value = '';
32503         
32504         if(!this.files || !this.files.length){
32505             return;
32506         }
32507         
32508         if(this.boxes > 0 && this.files.length > this.boxes){
32509             this.files = this.files.slice(0, this.boxes);
32510         }
32511         
32512         this.uploader.show();
32513         
32514         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32515             this.uploader.hide();
32516         }
32517         
32518         var _this = this;
32519         
32520         var files = [];
32521         
32522         var docs = [];
32523         
32524         Roo.each(this.files, function(file){
32525             
32526             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32527                 var f = this.renderPreview(file);
32528                 files.push(f);
32529                 return;
32530             }
32531             
32532             if(file.type.indexOf('image') != -1){
32533                 this.delegates.push(
32534                     (function(){
32535                         _this.process(file);
32536                     }).createDelegate(this)
32537                 );
32538         
32539                 return;
32540             }
32541             
32542             docs.push(
32543                 (function(){
32544                     _this.process(file);
32545                 }).createDelegate(this)
32546             );
32547             
32548         }, this);
32549         
32550         this.files = files;
32551         
32552         this.delegates = this.delegates.concat(docs);
32553         
32554         if(!this.delegates.length){
32555             this.refresh();
32556             return;
32557         }
32558         
32559         this.progressBar.aria_valuemax = this.delegates.length;
32560         
32561         this.arrange();
32562         
32563         return;
32564     },
32565     
32566     arrange : function()
32567     {
32568         if(!this.delegates.length){
32569             this.progressDialog.hide();
32570             this.refresh();
32571             return;
32572         }
32573         
32574         var delegate = this.delegates.shift();
32575         
32576         this.progressDialog.show();
32577         
32578         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32579         
32580         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32581         
32582         delegate();
32583     },
32584     
32585     refresh : function()
32586     {
32587         this.uploader.show();
32588         
32589         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32590             this.uploader.hide();
32591         }
32592         
32593         Roo.isTouch ? this.closable(false) : this.closable(true);
32594         
32595         this.fireEvent('refresh', this);
32596     },
32597     
32598     onRemove : function(e, el, o)
32599     {
32600         e.preventDefault();
32601         
32602         this.fireEvent('remove', this, o);
32603         
32604     },
32605     
32606     remove : function(o)
32607     {
32608         var files = [];
32609         
32610         Roo.each(this.files, function(file){
32611             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32612                 files.push(file);
32613                 return;
32614             }
32615
32616             o.target.remove();
32617
32618         }, this);
32619         
32620         this.files = files;
32621         
32622         this.refresh();
32623     },
32624     
32625     clear : function()
32626     {
32627         Roo.each(this.files, function(file){
32628             if(!file.target){
32629                 return;
32630             }
32631             
32632             file.target.remove();
32633
32634         }, this);
32635         
32636         this.files = [];
32637         
32638         this.refresh();
32639     },
32640     
32641     onClick : function(e, el, o)
32642     {
32643         e.preventDefault();
32644         
32645         this.fireEvent('click', this, o);
32646         
32647     },
32648     
32649     closable : function(closable)
32650     {
32651         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32652             
32653             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32654             
32655             if(closable){
32656                 el.show();
32657                 return;
32658             }
32659             
32660             el.hide();
32661             
32662         }, this);
32663     },
32664     
32665     xhrOnLoad : function(xhr)
32666     {
32667         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32668             el.remove();
32669         }, this);
32670         
32671         if (xhr.readyState !== 4) {
32672             this.arrange();
32673             this.fireEvent('exception', this, xhr);
32674             return;
32675         }
32676
32677         var response = Roo.decode(xhr.responseText);
32678         
32679         if(!response.success){
32680             this.arrange();
32681             this.fireEvent('exception', this, xhr);
32682             return;
32683         }
32684         
32685         var file = this.renderPreview(response.data);
32686         
32687         this.files.push(file);
32688         
32689         this.arrange();
32690         
32691         this.fireEvent('afterupload', this, xhr);
32692         
32693     },
32694     
32695     xhrOnError : function(xhr)
32696     {
32697         Roo.log('xhr on error');
32698         
32699         var response = Roo.decode(xhr.responseText);
32700           
32701         Roo.log(response);
32702         
32703         this.arrange();
32704     },
32705     
32706     process : function(file)
32707     {
32708         if(this.fireEvent('process', this, file) !== false){
32709             if(this.editable && file.type.indexOf('image') != -1){
32710                 this.fireEvent('edit', this, file);
32711                 return;
32712             }
32713
32714             this.uploadStart(file, false);
32715
32716             return;
32717         }
32718         
32719     },
32720     
32721     uploadStart : function(file, crop)
32722     {
32723         this.xhr = new XMLHttpRequest();
32724         
32725         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32726             this.arrange();
32727             return;
32728         }
32729         
32730         file.xhr = this.xhr;
32731             
32732         this.managerEl.createChild({
32733             tag : 'div',
32734             cls : 'roo-document-manager-loading',
32735             cn : [
32736                 {
32737                     tag : 'div',
32738                     tooltip : file.name,
32739                     cls : 'roo-document-manager-thumb',
32740                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32741                 }
32742             ]
32743
32744         });
32745
32746         this.xhr.open(this.method, this.url, true);
32747         
32748         var headers = {
32749             "Accept": "application/json",
32750             "Cache-Control": "no-cache",
32751             "X-Requested-With": "XMLHttpRequest"
32752         };
32753         
32754         for (var headerName in headers) {
32755             var headerValue = headers[headerName];
32756             if (headerValue) {
32757                 this.xhr.setRequestHeader(headerName, headerValue);
32758             }
32759         }
32760         
32761         var _this = this;
32762         
32763         this.xhr.onload = function()
32764         {
32765             _this.xhrOnLoad(_this.xhr);
32766         }
32767         
32768         this.xhr.onerror = function()
32769         {
32770             _this.xhrOnError(_this.xhr);
32771         }
32772         
32773         var formData = new FormData();
32774
32775         formData.append('returnHTML', 'NO');
32776         
32777         if(crop){
32778             formData.append('crop', crop);
32779         }
32780         
32781         formData.append(this.paramName, file, file.name);
32782         
32783         var options = {
32784             file : file, 
32785             manually : false
32786         };
32787         
32788         if(this.fireEvent('prepare', this, formData, options) != false){
32789             
32790             if(options.manually){
32791                 return;
32792             }
32793             
32794             this.xhr.send(formData);
32795             return;
32796         };
32797         
32798         this.uploadCancel();
32799     },
32800     
32801     uploadCancel : function()
32802     {
32803         if (this.xhr) {
32804             this.xhr.abort();
32805         }
32806         
32807         this.delegates = [];
32808         
32809         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32810             el.remove();
32811         }, this);
32812         
32813         this.arrange();
32814     },
32815     
32816     renderPreview : function(file)
32817     {
32818         if(typeof(file.target) != 'undefined' && file.target){
32819             return file;
32820         }
32821         
32822         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32823         
32824         var previewEl = this.managerEl.createChild({
32825             tag : 'div',
32826             cls : 'roo-document-manager-preview',
32827             cn : [
32828                 {
32829                     tag : 'div',
32830                     tooltip : file[this.toolTipName],
32831                     cls : 'roo-document-manager-thumb',
32832                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32833                 },
32834                 {
32835                     tag : 'button',
32836                     cls : 'close',
32837                     html : '<i class="fa fa-times-circle"></i>'
32838                 }
32839             ]
32840         });
32841
32842         var close = previewEl.select('button.close', true).first();
32843
32844         close.on('click', this.onRemove, this, file);
32845
32846         file.target = previewEl;
32847
32848         var image = previewEl.select('img', true).first();
32849         
32850         var _this = this;
32851         
32852         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32853         
32854         image.on('click', this.onClick, this, file);
32855         
32856         this.fireEvent('previewrendered', this, file);
32857         
32858         return file;
32859         
32860     },
32861     
32862     onPreviewLoad : function(file, image)
32863     {
32864         if(typeof(file.target) == 'undefined' || !file.target){
32865             return;
32866         }
32867         
32868         var width = image.dom.naturalWidth || image.dom.width;
32869         var height = image.dom.naturalHeight || image.dom.height;
32870         
32871         if(!this.previewResize) {
32872             return;
32873         }
32874         
32875         if(width > height){
32876             file.target.addClass('wide');
32877             return;
32878         }
32879         
32880         file.target.addClass('tall');
32881         return;
32882         
32883     },
32884     
32885     uploadFromSource : function(file, crop)
32886     {
32887         this.xhr = new XMLHttpRequest();
32888         
32889         this.managerEl.createChild({
32890             tag : 'div',
32891             cls : 'roo-document-manager-loading',
32892             cn : [
32893                 {
32894                     tag : 'div',
32895                     tooltip : file.name,
32896                     cls : 'roo-document-manager-thumb',
32897                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32898                 }
32899             ]
32900
32901         });
32902
32903         this.xhr.open(this.method, this.url, true);
32904         
32905         var headers = {
32906             "Accept": "application/json",
32907             "Cache-Control": "no-cache",
32908             "X-Requested-With": "XMLHttpRequest"
32909         };
32910         
32911         for (var headerName in headers) {
32912             var headerValue = headers[headerName];
32913             if (headerValue) {
32914                 this.xhr.setRequestHeader(headerName, headerValue);
32915             }
32916         }
32917         
32918         var _this = this;
32919         
32920         this.xhr.onload = function()
32921         {
32922             _this.xhrOnLoad(_this.xhr);
32923         }
32924         
32925         this.xhr.onerror = function()
32926         {
32927             _this.xhrOnError(_this.xhr);
32928         }
32929         
32930         var formData = new FormData();
32931
32932         formData.append('returnHTML', 'NO');
32933         
32934         formData.append('crop', crop);
32935         
32936         if(typeof(file.filename) != 'undefined'){
32937             formData.append('filename', file.filename);
32938         }
32939         
32940         if(typeof(file.mimetype) != 'undefined'){
32941             formData.append('mimetype', file.mimetype);
32942         }
32943         
32944         Roo.log(formData);
32945         
32946         if(this.fireEvent('prepare', this, formData) != false){
32947             this.xhr.send(formData);
32948         };
32949     }
32950 });
32951
32952 /*
32953 * Licence: LGPL
32954 */
32955
32956 /**
32957  * @class Roo.bootstrap.DocumentViewer
32958  * @extends Roo.bootstrap.Component
32959  * Bootstrap DocumentViewer class
32960  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32961  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32962  * 
32963  * @constructor
32964  * Create a new DocumentViewer
32965  * @param {Object} config The config object
32966  */
32967
32968 Roo.bootstrap.DocumentViewer = function(config){
32969     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32970     
32971     this.addEvents({
32972         /**
32973          * @event initial
32974          * Fire after initEvent
32975          * @param {Roo.bootstrap.DocumentViewer} this
32976          */
32977         "initial" : true,
32978         /**
32979          * @event click
32980          * Fire after click
32981          * @param {Roo.bootstrap.DocumentViewer} this
32982          */
32983         "click" : true,
32984         /**
32985          * @event download
32986          * Fire after download button
32987          * @param {Roo.bootstrap.DocumentViewer} this
32988          */
32989         "download" : true,
32990         /**
32991          * @event trash
32992          * Fire after trash button
32993          * @param {Roo.bootstrap.DocumentViewer} this
32994          */
32995         "trash" : true
32996         
32997     });
32998 };
32999
33000 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33001     
33002     showDownload : true,
33003     
33004     showTrash : true,
33005     
33006     getAutoCreate : function()
33007     {
33008         var cfg = {
33009             tag : 'div',
33010             cls : 'roo-document-viewer',
33011             cn : [
33012                 {
33013                     tag : 'div',
33014                     cls : 'roo-document-viewer-body',
33015                     cn : [
33016                         {
33017                             tag : 'div',
33018                             cls : 'roo-document-viewer-thumb',
33019                             cn : [
33020                                 {
33021                                     tag : 'img',
33022                                     cls : 'roo-document-viewer-image'
33023                                 }
33024                             ]
33025                         }
33026                     ]
33027                 },
33028                 {
33029                     tag : 'div',
33030                     cls : 'roo-document-viewer-footer',
33031                     cn : {
33032                         tag : 'div',
33033                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33034                         cn : [
33035                             {
33036                                 tag : 'div',
33037                                 cls : 'btn-group roo-document-viewer-download',
33038                                 cn : [
33039                                     {
33040                                         tag : 'button',
33041                                         cls : 'btn btn-default',
33042                                         html : '<i class="fa fa-download"></i>'
33043                                     }
33044                                 ]
33045                             },
33046                             {
33047                                 tag : 'div',
33048                                 cls : 'btn-group roo-document-viewer-trash',
33049                                 cn : [
33050                                     {
33051                                         tag : 'button',
33052                                         cls : 'btn btn-default',
33053                                         html : '<i class="fa fa-trash"></i>'
33054                                     }
33055                                 ]
33056                             }
33057                         ]
33058                     }
33059                 }
33060             ]
33061         };
33062         
33063         return cfg;
33064     },
33065     
33066     initEvents : function()
33067     {
33068         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33069         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33070         
33071         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33072         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33073         
33074         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33075         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33076         
33077         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33078         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33079         
33080         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33081         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33082         
33083         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33084         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33085         
33086         this.bodyEl.on('click', this.onClick, this);
33087         this.downloadBtn.on('click', this.onDownload, this);
33088         this.trashBtn.on('click', this.onTrash, this);
33089         
33090         this.downloadBtn.hide();
33091         this.trashBtn.hide();
33092         
33093         if(this.showDownload){
33094             this.downloadBtn.show();
33095         }
33096         
33097         if(this.showTrash){
33098             this.trashBtn.show();
33099         }
33100         
33101         if(!this.showDownload && !this.showTrash) {
33102             this.footerEl.hide();
33103         }
33104         
33105     },
33106     
33107     initial : function()
33108     {
33109         this.fireEvent('initial', this);
33110         
33111     },
33112     
33113     onClick : function(e)
33114     {
33115         e.preventDefault();
33116         
33117         this.fireEvent('click', this);
33118     },
33119     
33120     onDownload : function(e)
33121     {
33122         e.preventDefault();
33123         
33124         this.fireEvent('download', this);
33125     },
33126     
33127     onTrash : function(e)
33128     {
33129         e.preventDefault();
33130         
33131         this.fireEvent('trash', this);
33132     }
33133     
33134 });
33135 /*
33136  * - LGPL
33137  *
33138  * nav progress bar
33139  * 
33140  */
33141
33142 /**
33143  * @class Roo.bootstrap.NavProgressBar
33144  * @extends Roo.bootstrap.Component
33145  * Bootstrap NavProgressBar class
33146  * 
33147  * @constructor
33148  * Create a new nav progress bar
33149  * @param {Object} config The config object
33150  */
33151
33152 Roo.bootstrap.NavProgressBar = function(config){
33153     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33154
33155     this.bullets = this.bullets || [];
33156    
33157 //    Roo.bootstrap.NavProgressBar.register(this);
33158      this.addEvents({
33159         /**
33160              * @event changed
33161              * Fires when the active item changes
33162              * @param {Roo.bootstrap.NavProgressBar} this
33163              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33164              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33165          */
33166         'changed': true
33167      });
33168     
33169 };
33170
33171 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33172     
33173     bullets : [],
33174     barItems : [],
33175     
33176     getAutoCreate : function()
33177     {
33178         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33179         
33180         cfg = {
33181             tag : 'div',
33182             cls : 'roo-navigation-bar-group',
33183             cn : [
33184                 {
33185                     tag : 'div',
33186                     cls : 'roo-navigation-top-bar'
33187                 },
33188                 {
33189                     tag : 'div',
33190                     cls : 'roo-navigation-bullets-bar',
33191                     cn : [
33192                         {
33193                             tag : 'ul',
33194                             cls : 'roo-navigation-bar'
33195                         }
33196                     ]
33197                 },
33198                 
33199                 {
33200                     tag : 'div',
33201                     cls : 'roo-navigation-bottom-bar'
33202                 }
33203             ]
33204             
33205         };
33206         
33207         return cfg;
33208         
33209     },
33210     
33211     initEvents: function() 
33212     {
33213         
33214     },
33215     
33216     onRender : function(ct, position) 
33217     {
33218         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33219         
33220         if(this.bullets.length){
33221             Roo.each(this.bullets, function(b){
33222                this.addItem(b);
33223             }, this);
33224         }
33225         
33226         this.format();
33227         
33228     },
33229     
33230     addItem : function(cfg)
33231     {
33232         var item = new Roo.bootstrap.NavProgressItem(cfg);
33233         
33234         item.parentId = this.id;
33235         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33236         
33237         if(cfg.html){
33238             var top = new Roo.bootstrap.Element({
33239                 tag : 'div',
33240                 cls : 'roo-navigation-bar-text'
33241             });
33242             
33243             var bottom = new Roo.bootstrap.Element({
33244                 tag : 'div',
33245                 cls : 'roo-navigation-bar-text'
33246             });
33247             
33248             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33249             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33250             
33251             var topText = new Roo.bootstrap.Element({
33252                 tag : 'span',
33253                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33254             });
33255             
33256             var bottomText = new Roo.bootstrap.Element({
33257                 tag : 'span',
33258                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33259             });
33260             
33261             topText.onRender(top.el, null);
33262             bottomText.onRender(bottom.el, null);
33263             
33264             item.topEl = top;
33265             item.bottomEl = bottom;
33266         }
33267         
33268         this.barItems.push(item);
33269         
33270         return item;
33271     },
33272     
33273     getActive : function()
33274     {
33275         var active = false;
33276         
33277         Roo.each(this.barItems, function(v){
33278             
33279             if (!v.isActive()) {
33280                 return;
33281             }
33282             
33283             active = v;
33284             return false;
33285             
33286         });
33287         
33288         return active;
33289     },
33290     
33291     setActiveItem : function(item)
33292     {
33293         var prev = false;
33294         
33295         Roo.each(this.barItems, function(v){
33296             if (v.rid == item.rid) {
33297                 return ;
33298             }
33299             
33300             if (v.isActive()) {
33301                 v.setActive(false);
33302                 prev = v;
33303             }
33304         });
33305
33306         item.setActive(true);
33307         
33308         this.fireEvent('changed', this, item, prev);
33309     },
33310     
33311     getBarItem: function(rid)
33312     {
33313         var ret = false;
33314         
33315         Roo.each(this.barItems, function(e) {
33316             if (e.rid != rid) {
33317                 return;
33318             }
33319             
33320             ret =  e;
33321             return false;
33322         });
33323         
33324         return ret;
33325     },
33326     
33327     indexOfItem : function(item)
33328     {
33329         var index = false;
33330         
33331         Roo.each(this.barItems, function(v, i){
33332             
33333             if (v.rid != item.rid) {
33334                 return;
33335             }
33336             
33337             index = i;
33338             return false
33339         });
33340         
33341         return index;
33342     },
33343     
33344     setActiveNext : function()
33345     {
33346         var i = this.indexOfItem(this.getActive());
33347         
33348         if (i > this.barItems.length) {
33349             return;
33350         }
33351         
33352         this.setActiveItem(this.barItems[i+1]);
33353     },
33354     
33355     setActivePrev : function()
33356     {
33357         var i = this.indexOfItem(this.getActive());
33358         
33359         if (i  < 1) {
33360             return;
33361         }
33362         
33363         this.setActiveItem(this.barItems[i-1]);
33364     },
33365     
33366     format : function()
33367     {
33368         if(!this.barItems.length){
33369             return;
33370         }
33371      
33372         var width = 100 / this.barItems.length;
33373         
33374         Roo.each(this.barItems, function(i){
33375             i.el.setStyle('width', width + '%');
33376             i.topEl.el.setStyle('width', width + '%');
33377             i.bottomEl.el.setStyle('width', width + '%');
33378         }, this);
33379         
33380     }
33381     
33382 });
33383 /*
33384  * - LGPL
33385  *
33386  * Nav Progress Item
33387  * 
33388  */
33389
33390 /**
33391  * @class Roo.bootstrap.NavProgressItem
33392  * @extends Roo.bootstrap.Component
33393  * Bootstrap NavProgressItem class
33394  * @cfg {String} rid the reference id
33395  * @cfg {Boolean} active (true|false) Is item active default false
33396  * @cfg {Boolean} disabled (true|false) Is item active default false
33397  * @cfg {String} html
33398  * @cfg {String} position (top|bottom) text position default bottom
33399  * @cfg {String} icon show icon instead of number
33400  * 
33401  * @constructor
33402  * Create a new NavProgressItem
33403  * @param {Object} config The config object
33404  */
33405 Roo.bootstrap.NavProgressItem = function(config){
33406     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33407     this.addEvents({
33408         // raw events
33409         /**
33410          * @event click
33411          * The raw click event for the entire grid.
33412          * @param {Roo.bootstrap.NavProgressItem} this
33413          * @param {Roo.EventObject} e
33414          */
33415         "click" : true
33416     });
33417    
33418 };
33419
33420 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33421     
33422     rid : '',
33423     active : false,
33424     disabled : false,
33425     html : '',
33426     position : 'bottom',
33427     icon : false,
33428     
33429     getAutoCreate : function()
33430     {
33431         var iconCls = 'roo-navigation-bar-item-icon';
33432         
33433         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33434         
33435         var cfg = {
33436             tag: 'li',
33437             cls: 'roo-navigation-bar-item',
33438             cn : [
33439                 {
33440                     tag : 'i',
33441                     cls : iconCls
33442                 }
33443             ]
33444         };
33445         
33446         if(this.active){
33447             cfg.cls += ' active';
33448         }
33449         if(this.disabled){
33450             cfg.cls += ' disabled';
33451         }
33452         
33453         return cfg;
33454     },
33455     
33456     disable : function()
33457     {
33458         this.setDisabled(true);
33459     },
33460     
33461     enable : function()
33462     {
33463         this.setDisabled(false);
33464     },
33465     
33466     initEvents: function() 
33467     {
33468         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33469         
33470         this.iconEl.on('click', this.onClick, this);
33471     },
33472     
33473     onClick : function(e)
33474     {
33475         e.preventDefault();
33476         
33477         if(this.disabled){
33478             return;
33479         }
33480         
33481         if(this.fireEvent('click', this, e) === false){
33482             return;
33483         };
33484         
33485         this.parent().setActiveItem(this);
33486     },
33487     
33488     isActive: function () 
33489     {
33490         return this.active;
33491     },
33492     
33493     setActive : function(state)
33494     {
33495         if(this.active == state){
33496             return;
33497         }
33498         
33499         this.active = state;
33500         
33501         if (state) {
33502             this.el.addClass('active');
33503             return;
33504         }
33505         
33506         this.el.removeClass('active');
33507         
33508         return;
33509     },
33510     
33511     setDisabled : function(state)
33512     {
33513         if(this.disabled == state){
33514             return;
33515         }
33516         
33517         this.disabled = state;
33518         
33519         if (state) {
33520             this.el.addClass('disabled');
33521             return;
33522         }
33523         
33524         this.el.removeClass('disabled');
33525     },
33526     
33527     tooltipEl : function()
33528     {
33529         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33530     }
33531 });
33532  
33533
33534  /*
33535  * - LGPL
33536  *
33537  * FieldLabel
33538  * 
33539  */
33540
33541 /**
33542  * @class Roo.bootstrap.FieldLabel
33543  * @extends Roo.bootstrap.Component
33544  * Bootstrap FieldLabel class
33545  * @cfg {String} html contents of the element
33546  * @cfg {String} tag tag of the element default label
33547  * @cfg {String} cls class of the element
33548  * @cfg {String} target label target 
33549  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33550  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33551  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33552  * @cfg {String} iconTooltip default "This field is required"
33553  * @cfg {String} indicatorpos (left|right) default left
33554  * 
33555  * @constructor
33556  * Create a new FieldLabel
33557  * @param {Object} config The config object
33558  */
33559
33560 Roo.bootstrap.FieldLabel = function(config){
33561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33562     
33563     this.addEvents({
33564             /**
33565              * @event invalid
33566              * Fires after the field has been marked as invalid.
33567              * @param {Roo.form.FieldLabel} this
33568              * @param {String} msg The validation message
33569              */
33570             invalid : true,
33571             /**
33572              * @event valid
33573              * Fires after the field has been validated with no errors.
33574              * @param {Roo.form.FieldLabel} this
33575              */
33576             valid : true
33577         });
33578 };
33579
33580 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33581     
33582     tag: 'label',
33583     cls: '',
33584     html: '',
33585     target: '',
33586     allowBlank : true,
33587     invalidClass : 'has-warning',
33588     validClass : 'has-success',
33589     iconTooltip : 'This field is required',
33590     indicatorpos : 'left',
33591     
33592     getAutoCreate : function(){
33593         
33594         var cls = "";
33595         if (!this.allowBlank) {
33596             cls  = "visible";
33597         }
33598         
33599         var cfg = {
33600             tag : this.tag,
33601             cls : 'roo-bootstrap-field-label ' + this.cls,
33602             for : this.target,
33603             cn : [
33604                 {
33605                     tag : 'i',
33606                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33607                     tooltip : this.iconTooltip
33608                 },
33609                 {
33610                     tag : 'span',
33611                     html : this.html
33612                 }
33613             ] 
33614         };
33615         
33616         if(this.indicatorpos == 'right'){
33617             var cfg = {
33618                 tag : this.tag,
33619                 cls : 'roo-bootstrap-field-label ' + this.cls,
33620                 for : this.target,
33621                 cn : [
33622                     {
33623                         tag : 'span',
33624                         html : this.html
33625                     },
33626                     {
33627                         tag : 'i',
33628                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33629                         tooltip : this.iconTooltip
33630                     }
33631                 ] 
33632             };
33633         }
33634         
33635         return cfg;
33636     },
33637     
33638     initEvents: function() 
33639     {
33640         Roo.bootstrap.Element.superclass.initEvents.call(this);
33641         
33642         this.indicator = this.indicatorEl();
33643         
33644         if(this.indicator){
33645             this.indicator.removeClass('visible');
33646             this.indicator.addClass('invisible');
33647         }
33648         
33649         Roo.bootstrap.FieldLabel.register(this);
33650     },
33651     
33652     indicatorEl : function()
33653     {
33654         var indicator = this.el.select('i.roo-required-indicator',true).first();
33655         
33656         if(!indicator){
33657             return false;
33658         }
33659         
33660         return indicator;
33661         
33662     },
33663     
33664     /**
33665      * Mark this field as valid
33666      */
33667     markValid : function()
33668     {
33669         if(this.indicator){
33670             this.indicator.removeClass('visible');
33671             this.indicator.addClass('invisible');
33672         }
33673         if (Roo.bootstrap.version == 3) {
33674             this.el.removeClass(this.invalidClass);
33675             this.el.addClass(this.validClass);
33676         } else {
33677             this.el.removeClass('is-invalid');
33678             this.el.addClass('is-valid');
33679         }
33680         
33681         
33682         this.fireEvent('valid', this);
33683     },
33684     
33685     /**
33686      * Mark this field as invalid
33687      * @param {String} msg The validation message
33688      */
33689     markInvalid : function(msg)
33690     {
33691         if(this.indicator){
33692             this.indicator.removeClass('invisible');
33693             this.indicator.addClass('visible');
33694         }
33695           if (Roo.bootstrap.version == 3) {
33696             this.el.removeClass(this.validClass);
33697             this.el.addClass(this.invalidClass);
33698         } else {
33699             this.el.removeClass('is-valid');
33700             this.el.addClass('is-invalid');
33701         }
33702         
33703         
33704         this.fireEvent('invalid', this, msg);
33705     }
33706     
33707    
33708 });
33709
33710 Roo.apply(Roo.bootstrap.FieldLabel, {
33711     
33712     groups: {},
33713     
33714      /**
33715     * register a FieldLabel Group
33716     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33717     */
33718     register : function(label)
33719     {
33720         if(this.groups.hasOwnProperty(label.target)){
33721             return;
33722         }
33723      
33724         this.groups[label.target] = label;
33725         
33726     },
33727     /**
33728     * fetch a FieldLabel Group based on the target
33729     * @param {string} target
33730     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33731     */
33732     get: function(target) {
33733         if (typeof(this.groups[target]) == 'undefined') {
33734             return false;
33735         }
33736         
33737         return this.groups[target] ;
33738     }
33739 });
33740
33741  
33742
33743  /*
33744  * - LGPL
33745  *
33746  * page DateSplitField.
33747  * 
33748  */
33749
33750
33751 /**
33752  * @class Roo.bootstrap.DateSplitField
33753  * @extends Roo.bootstrap.Component
33754  * Bootstrap DateSplitField class
33755  * @cfg {string} fieldLabel - the label associated
33756  * @cfg {Number} labelWidth set the width of label (0-12)
33757  * @cfg {String} labelAlign (top|left)
33758  * @cfg {Boolean} dayAllowBlank (true|false) default false
33759  * @cfg {Boolean} monthAllowBlank (true|false) default false
33760  * @cfg {Boolean} yearAllowBlank (true|false) default false
33761  * @cfg {string} dayPlaceholder 
33762  * @cfg {string} monthPlaceholder
33763  * @cfg {string} yearPlaceholder
33764  * @cfg {string} dayFormat default 'd'
33765  * @cfg {string} monthFormat default 'm'
33766  * @cfg {string} yearFormat default 'Y'
33767  * @cfg {Number} labellg set the width of label (1-12)
33768  * @cfg {Number} labelmd set the width of label (1-12)
33769  * @cfg {Number} labelsm set the width of label (1-12)
33770  * @cfg {Number} labelxs set the width of label (1-12)
33771
33772  *     
33773  * @constructor
33774  * Create a new DateSplitField
33775  * @param {Object} config The config object
33776  */
33777
33778 Roo.bootstrap.DateSplitField = function(config){
33779     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33780     
33781     this.addEvents({
33782         // raw events
33783          /**
33784          * @event years
33785          * getting the data of years
33786          * @param {Roo.bootstrap.DateSplitField} this
33787          * @param {Object} years
33788          */
33789         "years" : true,
33790         /**
33791          * @event days
33792          * getting the data of days
33793          * @param {Roo.bootstrap.DateSplitField} this
33794          * @param {Object} days
33795          */
33796         "days" : true,
33797         /**
33798          * @event invalid
33799          * Fires after the field has been marked as invalid.
33800          * @param {Roo.form.Field} this
33801          * @param {String} msg The validation message
33802          */
33803         invalid : true,
33804        /**
33805          * @event valid
33806          * Fires after the field has been validated with no errors.
33807          * @param {Roo.form.Field} this
33808          */
33809         valid : true
33810     });
33811 };
33812
33813 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33814     
33815     fieldLabel : '',
33816     labelAlign : 'top',
33817     labelWidth : 3,
33818     dayAllowBlank : false,
33819     monthAllowBlank : false,
33820     yearAllowBlank : false,
33821     dayPlaceholder : '',
33822     monthPlaceholder : '',
33823     yearPlaceholder : '',
33824     dayFormat : 'd',
33825     monthFormat : 'm',
33826     yearFormat : 'Y',
33827     isFormField : true,
33828     labellg : 0,
33829     labelmd : 0,
33830     labelsm : 0,
33831     labelxs : 0,
33832     
33833     getAutoCreate : function()
33834     {
33835         var cfg = {
33836             tag : 'div',
33837             cls : 'row roo-date-split-field-group',
33838             cn : [
33839                 {
33840                     tag : 'input',
33841                     type : 'hidden',
33842                     cls : 'form-hidden-field roo-date-split-field-group-value',
33843                     name : this.name
33844                 }
33845             ]
33846         };
33847         
33848         var labelCls = 'col-md-12';
33849         var contentCls = 'col-md-4';
33850         
33851         if(this.fieldLabel){
33852             
33853             var label = {
33854                 tag : 'div',
33855                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33856                 cn : [
33857                     {
33858                         tag : 'label',
33859                         html : this.fieldLabel
33860                     }
33861                 ]
33862             };
33863             
33864             if(this.labelAlign == 'left'){
33865             
33866                 if(this.labelWidth > 12){
33867                     label.style = "width: " + this.labelWidth + 'px';
33868                 }
33869
33870                 if(this.labelWidth < 13 && this.labelmd == 0){
33871                     this.labelmd = this.labelWidth;
33872                 }
33873
33874                 if(this.labellg > 0){
33875                     labelCls = ' col-lg-' + this.labellg;
33876                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33877                 }
33878
33879                 if(this.labelmd > 0){
33880                     labelCls = ' col-md-' + this.labelmd;
33881                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33882                 }
33883
33884                 if(this.labelsm > 0){
33885                     labelCls = ' col-sm-' + this.labelsm;
33886                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33887                 }
33888
33889                 if(this.labelxs > 0){
33890                     labelCls = ' col-xs-' + this.labelxs;
33891                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33892                 }
33893             }
33894             
33895             label.cls += ' ' + labelCls;
33896             
33897             cfg.cn.push(label);
33898         }
33899         
33900         Roo.each(['day', 'month', 'year'], function(t){
33901             cfg.cn.push({
33902                 tag : 'div',
33903                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33904             });
33905         }, this);
33906         
33907         return cfg;
33908     },
33909     
33910     inputEl: function ()
33911     {
33912         return this.el.select('.roo-date-split-field-group-value', true).first();
33913     },
33914     
33915     onRender : function(ct, position) 
33916     {
33917         var _this = this;
33918         
33919         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33920         
33921         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33922         
33923         this.dayField = new Roo.bootstrap.ComboBox({
33924             allowBlank : this.dayAllowBlank,
33925             alwaysQuery : true,
33926             displayField : 'value',
33927             editable : false,
33928             fieldLabel : '',
33929             forceSelection : true,
33930             mode : 'local',
33931             placeholder : this.dayPlaceholder,
33932             selectOnFocus : true,
33933             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33934             triggerAction : 'all',
33935             typeAhead : true,
33936             valueField : 'value',
33937             store : new Roo.data.SimpleStore({
33938                 data : (function() {    
33939                     var days = [];
33940                     _this.fireEvent('days', _this, days);
33941                     return days;
33942                 })(),
33943                 fields : [ 'value' ]
33944             }),
33945             listeners : {
33946                 select : function (_self, record, index)
33947                 {
33948                     _this.setValue(_this.getValue());
33949                 }
33950             }
33951         });
33952
33953         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33954         
33955         this.monthField = new Roo.bootstrap.MonthField({
33956             after : '<i class=\"fa fa-calendar\"></i>',
33957             allowBlank : this.monthAllowBlank,
33958             placeholder : this.monthPlaceholder,
33959             readOnly : true,
33960             listeners : {
33961                 render : function (_self)
33962                 {
33963                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33964                         e.preventDefault();
33965                         _self.focus();
33966                     });
33967                 },
33968                 select : function (_self, oldvalue, newvalue)
33969                 {
33970                     _this.setValue(_this.getValue());
33971                 }
33972             }
33973         });
33974         
33975         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33976         
33977         this.yearField = new Roo.bootstrap.ComboBox({
33978             allowBlank : this.yearAllowBlank,
33979             alwaysQuery : true,
33980             displayField : 'value',
33981             editable : false,
33982             fieldLabel : '',
33983             forceSelection : true,
33984             mode : 'local',
33985             placeholder : this.yearPlaceholder,
33986             selectOnFocus : true,
33987             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33988             triggerAction : 'all',
33989             typeAhead : true,
33990             valueField : 'value',
33991             store : new Roo.data.SimpleStore({
33992                 data : (function() {
33993                     var years = [];
33994                     _this.fireEvent('years', _this, years);
33995                     return years;
33996                 })(),
33997                 fields : [ 'value' ]
33998             }),
33999             listeners : {
34000                 select : function (_self, record, index)
34001                 {
34002                     _this.setValue(_this.getValue());
34003                 }
34004             }
34005         });
34006
34007         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34008     },
34009     
34010     setValue : function(v, format)
34011     {
34012         this.inputEl.dom.value = v;
34013         
34014         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34015         
34016         var d = Date.parseDate(v, f);
34017         
34018         if(!d){
34019             this.validate();
34020             return;
34021         }
34022         
34023         this.setDay(d.format(this.dayFormat));
34024         this.setMonth(d.format(this.monthFormat));
34025         this.setYear(d.format(this.yearFormat));
34026         
34027         this.validate();
34028         
34029         return;
34030     },
34031     
34032     setDay : function(v)
34033     {
34034         this.dayField.setValue(v);
34035         this.inputEl.dom.value = this.getValue();
34036         this.validate();
34037         return;
34038     },
34039     
34040     setMonth : function(v)
34041     {
34042         this.monthField.setValue(v, true);
34043         this.inputEl.dom.value = this.getValue();
34044         this.validate();
34045         return;
34046     },
34047     
34048     setYear : function(v)
34049     {
34050         this.yearField.setValue(v);
34051         this.inputEl.dom.value = this.getValue();
34052         this.validate();
34053         return;
34054     },
34055     
34056     getDay : function()
34057     {
34058         return this.dayField.getValue();
34059     },
34060     
34061     getMonth : function()
34062     {
34063         return this.monthField.getValue();
34064     },
34065     
34066     getYear : function()
34067     {
34068         return this.yearField.getValue();
34069     },
34070     
34071     getValue : function()
34072     {
34073         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34074         
34075         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34076         
34077         return date;
34078     },
34079     
34080     reset : function()
34081     {
34082         this.setDay('');
34083         this.setMonth('');
34084         this.setYear('');
34085         this.inputEl.dom.value = '';
34086         this.validate();
34087         return;
34088     },
34089     
34090     validate : function()
34091     {
34092         var d = this.dayField.validate();
34093         var m = this.monthField.validate();
34094         var y = this.yearField.validate();
34095         
34096         var valid = true;
34097         
34098         if(
34099                 (!this.dayAllowBlank && !d) ||
34100                 (!this.monthAllowBlank && !m) ||
34101                 (!this.yearAllowBlank && !y)
34102         ){
34103             valid = false;
34104         }
34105         
34106         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34107             return valid;
34108         }
34109         
34110         if(valid){
34111             this.markValid();
34112             return valid;
34113         }
34114         
34115         this.markInvalid();
34116         
34117         return valid;
34118     },
34119     
34120     markValid : function()
34121     {
34122         
34123         var label = this.el.select('label', true).first();
34124         var icon = this.el.select('i.fa-star', true).first();
34125
34126         if(label && icon){
34127             icon.remove();
34128         }
34129         
34130         this.fireEvent('valid', this);
34131     },
34132     
34133      /**
34134      * Mark this field as invalid
34135      * @param {String} msg The validation message
34136      */
34137     markInvalid : function(msg)
34138     {
34139         
34140         var label = this.el.select('label', true).first();
34141         var icon = this.el.select('i.fa-star', true).first();
34142
34143         if(label && !icon){
34144             this.el.select('.roo-date-split-field-label', true).createChild({
34145                 tag : 'i',
34146                 cls : 'text-danger fa fa-lg fa-star',
34147                 tooltip : 'This field is required',
34148                 style : 'margin-right:5px;'
34149             }, label, true);
34150         }
34151         
34152         this.fireEvent('invalid', this, msg);
34153     },
34154     
34155     clearInvalid : function()
34156     {
34157         var label = this.el.select('label', true).first();
34158         var icon = this.el.select('i.fa-star', true).first();
34159
34160         if(label && icon){
34161             icon.remove();
34162         }
34163         
34164         this.fireEvent('valid', this);
34165     },
34166     
34167     getName: function()
34168     {
34169         return this.name;
34170     }
34171     
34172 });
34173
34174  /**
34175  *
34176  * This is based on 
34177  * http://masonry.desandro.com
34178  *
34179  * The idea is to render all the bricks based on vertical width...
34180  *
34181  * The original code extends 'outlayer' - we might need to use that....
34182  * 
34183  */
34184
34185
34186 /**
34187  * @class Roo.bootstrap.LayoutMasonry
34188  * @extends Roo.bootstrap.Component
34189  * Bootstrap Layout Masonry class
34190  * 
34191  * @constructor
34192  * Create a new Element
34193  * @param {Object} config The config object
34194  */
34195
34196 Roo.bootstrap.LayoutMasonry = function(config){
34197     
34198     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34199     
34200     this.bricks = [];
34201     
34202     Roo.bootstrap.LayoutMasonry.register(this);
34203     
34204     this.addEvents({
34205         // raw events
34206         /**
34207          * @event layout
34208          * Fire after layout the items
34209          * @param {Roo.bootstrap.LayoutMasonry} this
34210          * @param {Roo.EventObject} e
34211          */
34212         "layout" : true
34213     });
34214     
34215 };
34216
34217 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34218     
34219     /**
34220      * @cfg {Boolean} isLayoutInstant = no animation?
34221      */   
34222     isLayoutInstant : false, // needed?
34223    
34224     /**
34225      * @cfg {Number} boxWidth  width of the columns
34226      */   
34227     boxWidth : 450,
34228     
34229       /**
34230      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34231      */   
34232     boxHeight : 0,
34233     
34234     /**
34235      * @cfg {Number} padWidth padding below box..
34236      */   
34237     padWidth : 10, 
34238     
34239     /**
34240      * @cfg {Number} gutter gutter width..
34241      */   
34242     gutter : 10,
34243     
34244      /**
34245      * @cfg {Number} maxCols maximum number of columns
34246      */   
34247     
34248     maxCols: 0,
34249     
34250     /**
34251      * @cfg {Boolean} isAutoInitial defalut true
34252      */   
34253     isAutoInitial : true, 
34254     
34255     containerWidth: 0,
34256     
34257     /**
34258      * @cfg {Boolean} isHorizontal defalut false
34259      */   
34260     isHorizontal : false, 
34261
34262     currentSize : null,
34263     
34264     tag: 'div',
34265     
34266     cls: '',
34267     
34268     bricks: null, //CompositeElement
34269     
34270     cols : 1,
34271     
34272     _isLayoutInited : false,
34273     
34274 //    isAlternative : false, // only use for vertical layout...
34275     
34276     /**
34277      * @cfg {Number} alternativePadWidth padding below box..
34278      */   
34279     alternativePadWidth : 50,
34280     
34281     selectedBrick : [],
34282     
34283     getAutoCreate : function(){
34284         
34285         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34286         
34287         var cfg = {
34288             tag: this.tag,
34289             cls: 'blog-masonary-wrapper ' + this.cls,
34290             cn : {
34291                 cls : 'mas-boxes masonary'
34292             }
34293         };
34294         
34295         return cfg;
34296     },
34297     
34298     getChildContainer: function( )
34299     {
34300         if (this.boxesEl) {
34301             return this.boxesEl;
34302         }
34303         
34304         this.boxesEl = this.el.select('.mas-boxes').first();
34305         
34306         return this.boxesEl;
34307     },
34308     
34309     
34310     initEvents : function()
34311     {
34312         var _this = this;
34313         
34314         if(this.isAutoInitial){
34315             Roo.log('hook children rendered');
34316             this.on('childrenrendered', function() {
34317                 Roo.log('children rendered');
34318                 _this.initial();
34319             } ,this);
34320         }
34321     },
34322     
34323     initial : function()
34324     {
34325         this.selectedBrick = [];
34326         
34327         this.currentSize = this.el.getBox(true);
34328         
34329         Roo.EventManager.onWindowResize(this.resize, this); 
34330
34331         if(!this.isAutoInitial){
34332             this.layout();
34333             return;
34334         }
34335         
34336         this.layout();
34337         
34338         return;
34339         //this.layout.defer(500,this);
34340         
34341     },
34342     
34343     resize : function()
34344     {
34345         var cs = this.el.getBox(true);
34346         
34347         if (
34348                 this.currentSize.width == cs.width && 
34349                 this.currentSize.x == cs.x && 
34350                 this.currentSize.height == cs.height && 
34351                 this.currentSize.y == cs.y 
34352         ) {
34353             Roo.log("no change in with or X or Y");
34354             return;
34355         }
34356         
34357         this.currentSize = cs;
34358         
34359         this.layout();
34360         
34361     },
34362     
34363     layout : function()
34364     {   
34365         this._resetLayout();
34366         
34367         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34368         
34369         this.layoutItems( isInstant );
34370       
34371         this._isLayoutInited = true;
34372         
34373         this.fireEvent('layout', this);
34374         
34375     },
34376     
34377     _resetLayout : function()
34378     {
34379         if(this.isHorizontal){
34380             this.horizontalMeasureColumns();
34381             return;
34382         }
34383         
34384         this.verticalMeasureColumns();
34385         
34386     },
34387     
34388     verticalMeasureColumns : function()
34389     {
34390         this.getContainerWidth();
34391         
34392 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34393 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34394 //            return;
34395 //        }
34396         
34397         var boxWidth = this.boxWidth + this.padWidth;
34398         
34399         if(this.containerWidth < this.boxWidth){
34400             boxWidth = this.containerWidth
34401         }
34402         
34403         var containerWidth = this.containerWidth;
34404         
34405         var cols = Math.floor(containerWidth / boxWidth);
34406         
34407         this.cols = Math.max( cols, 1 );
34408         
34409         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34410         
34411         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34412         
34413         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34414         
34415         this.colWidth = boxWidth + avail - this.padWidth;
34416         
34417         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34418         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34419     },
34420     
34421     horizontalMeasureColumns : function()
34422     {
34423         this.getContainerWidth();
34424         
34425         var boxWidth = this.boxWidth;
34426         
34427         if(this.containerWidth < boxWidth){
34428             boxWidth = this.containerWidth;
34429         }
34430         
34431         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34432         
34433         this.el.setHeight(boxWidth);
34434         
34435     },
34436     
34437     getContainerWidth : function()
34438     {
34439         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34440     },
34441     
34442     layoutItems : function( isInstant )
34443     {
34444         Roo.log(this.bricks);
34445         
34446         var items = Roo.apply([], this.bricks);
34447         
34448         if(this.isHorizontal){
34449             this._horizontalLayoutItems( items , isInstant );
34450             return;
34451         }
34452         
34453 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34454 //            this._verticalAlternativeLayoutItems( items , isInstant );
34455 //            return;
34456 //        }
34457         
34458         this._verticalLayoutItems( items , isInstant );
34459         
34460     },
34461     
34462     _verticalLayoutItems : function ( items , isInstant)
34463     {
34464         if ( !items || !items.length ) {
34465             return;
34466         }
34467         
34468         var standard = [
34469             ['xs', 'xs', 'xs', 'tall'],
34470             ['xs', 'xs', 'tall'],
34471             ['xs', 'xs', 'sm'],
34472             ['xs', 'xs', 'xs'],
34473             ['xs', 'tall'],
34474             ['xs', 'sm'],
34475             ['xs', 'xs'],
34476             ['xs'],
34477             
34478             ['sm', 'xs', 'xs'],
34479             ['sm', 'xs'],
34480             ['sm'],
34481             
34482             ['tall', 'xs', 'xs', 'xs'],
34483             ['tall', 'xs', 'xs'],
34484             ['tall', 'xs'],
34485             ['tall']
34486             
34487         ];
34488         
34489         var queue = [];
34490         
34491         var boxes = [];
34492         
34493         var box = [];
34494         
34495         Roo.each(items, function(item, k){
34496             
34497             switch (item.size) {
34498                 // these layouts take up a full box,
34499                 case 'md' :
34500                 case 'md-left' :
34501                 case 'md-right' :
34502                 case 'wide' :
34503                     
34504                     if(box.length){
34505                         boxes.push(box);
34506                         box = [];
34507                     }
34508                     
34509                     boxes.push([item]);
34510                     
34511                     break;
34512                     
34513                 case 'xs' :
34514                 case 'sm' :
34515                 case 'tall' :
34516                     
34517                     box.push(item);
34518                     
34519                     break;
34520                 default :
34521                     break;
34522                     
34523             }
34524             
34525         }, this);
34526         
34527         if(box.length){
34528             boxes.push(box);
34529             box = [];
34530         }
34531         
34532         var filterPattern = function(box, length)
34533         {
34534             if(!box.length){
34535                 return;
34536             }
34537             
34538             var match = false;
34539             
34540             var pattern = box.slice(0, length);
34541             
34542             var format = [];
34543             
34544             Roo.each(pattern, function(i){
34545                 format.push(i.size);
34546             }, this);
34547             
34548             Roo.each(standard, function(s){
34549                 
34550                 if(String(s) != String(format)){
34551                     return;
34552                 }
34553                 
34554                 match = true;
34555                 return false;
34556                 
34557             }, this);
34558             
34559             if(!match && length == 1){
34560                 return;
34561             }
34562             
34563             if(!match){
34564                 filterPattern(box, length - 1);
34565                 return;
34566             }
34567                 
34568             queue.push(pattern);
34569
34570             box = box.slice(length, box.length);
34571
34572             filterPattern(box, 4);
34573
34574             return;
34575             
34576         }
34577         
34578         Roo.each(boxes, function(box, k){
34579             
34580             if(!box.length){
34581                 return;
34582             }
34583             
34584             if(box.length == 1){
34585                 queue.push(box);
34586                 return;
34587             }
34588             
34589             filterPattern(box, 4);
34590             
34591         }, this);
34592         
34593         this._processVerticalLayoutQueue( queue, isInstant );
34594         
34595     },
34596     
34597 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34598 //    {
34599 //        if ( !items || !items.length ) {
34600 //            return;
34601 //        }
34602 //
34603 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34604 //        
34605 //    },
34606     
34607     _horizontalLayoutItems : function ( items , isInstant)
34608     {
34609         if ( !items || !items.length || items.length < 3) {
34610             return;
34611         }
34612         
34613         items.reverse();
34614         
34615         var eItems = items.slice(0, 3);
34616         
34617         items = items.slice(3, items.length);
34618         
34619         var standard = [
34620             ['xs', 'xs', 'xs', 'wide'],
34621             ['xs', 'xs', 'wide'],
34622             ['xs', 'xs', 'sm'],
34623             ['xs', 'xs', 'xs'],
34624             ['xs', 'wide'],
34625             ['xs', 'sm'],
34626             ['xs', 'xs'],
34627             ['xs'],
34628             
34629             ['sm', 'xs', 'xs'],
34630             ['sm', 'xs'],
34631             ['sm'],
34632             
34633             ['wide', 'xs', 'xs', 'xs'],
34634             ['wide', 'xs', 'xs'],
34635             ['wide', 'xs'],
34636             ['wide'],
34637             
34638             ['wide-thin']
34639         ];
34640         
34641         var queue = [];
34642         
34643         var boxes = [];
34644         
34645         var box = [];
34646         
34647         Roo.each(items, function(item, k){
34648             
34649             switch (item.size) {
34650                 case 'md' :
34651                 case 'md-left' :
34652                 case 'md-right' :
34653                 case 'tall' :
34654                     
34655                     if(box.length){
34656                         boxes.push(box);
34657                         box = [];
34658                     }
34659                     
34660                     boxes.push([item]);
34661                     
34662                     break;
34663                     
34664                 case 'xs' :
34665                 case 'sm' :
34666                 case 'wide' :
34667                 case 'wide-thin' :
34668                     
34669                     box.push(item);
34670                     
34671                     break;
34672                 default :
34673                     break;
34674                     
34675             }
34676             
34677         }, this);
34678         
34679         if(box.length){
34680             boxes.push(box);
34681             box = [];
34682         }
34683         
34684         var filterPattern = function(box, length)
34685         {
34686             if(!box.length){
34687                 return;
34688             }
34689             
34690             var match = false;
34691             
34692             var pattern = box.slice(0, length);
34693             
34694             var format = [];
34695             
34696             Roo.each(pattern, function(i){
34697                 format.push(i.size);
34698             }, this);
34699             
34700             Roo.each(standard, function(s){
34701                 
34702                 if(String(s) != String(format)){
34703                     return;
34704                 }
34705                 
34706                 match = true;
34707                 return false;
34708                 
34709             }, this);
34710             
34711             if(!match && length == 1){
34712                 return;
34713             }
34714             
34715             if(!match){
34716                 filterPattern(box, length - 1);
34717                 return;
34718             }
34719                 
34720             queue.push(pattern);
34721
34722             box = box.slice(length, box.length);
34723
34724             filterPattern(box, 4);
34725
34726             return;
34727             
34728         }
34729         
34730         Roo.each(boxes, function(box, k){
34731             
34732             if(!box.length){
34733                 return;
34734             }
34735             
34736             if(box.length == 1){
34737                 queue.push(box);
34738                 return;
34739             }
34740             
34741             filterPattern(box, 4);
34742             
34743         }, this);
34744         
34745         
34746         var prune = [];
34747         
34748         var pos = this.el.getBox(true);
34749         
34750         var minX = pos.x;
34751         
34752         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34753         
34754         var hit_end = false;
34755         
34756         Roo.each(queue, function(box){
34757             
34758             if(hit_end){
34759                 
34760                 Roo.each(box, function(b){
34761                 
34762                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34763                     b.el.hide();
34764
34765                 }, this);
34766
34767                 return;
34768             }
34769             
34770             var mx = 0;
34771             
34772             Roo.each(box, function(b){
34773                 
34774                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34775                 b.el.show();
34776
34777                 mx = Math.max(mx, b.x);
34778                 
34779             }, this);
34780             
34781             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34782             
34783             if(maxX < minX){
34784                 
34785                 Roo.each(box, function(b){
34786                 
34787                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34788                     b.el.hide();
34789                     
34790                 }, this);
34791                 
34792                 hit_end = true;
34793                 
34794                 return;
34795             }
34796             
34797             prune.push(box);
34798             
34799         }, this);
34800         
34801         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34802     },
34803     
34804     /** Sets position of item in DOM
34805     * @param {Element} item
34806     * @param {Number} x - horizontal position
34807     * @param {Number} y - vertical position
34808     * @param {Boolean} isInstant - disables transitions
34809     */
34810     _processVerticalLayoutQueue : function( queue, isInstant )
34811     {
34812         var pos = this.el.getBox(true);
34813         var x = pos.x;
34814         var y = pos.y;
34815         var maxY = [];
34816         
34817         for (var i = 0; i < this.cols; i++){
34818             maxY[i] = pos.y;
34819         }
34820         
34821         Roo.each(queue, function(box, k){
34822             
34823             var col = k % this.cols;
34824             
34825             Roo.each(box, function(b,kk){
34826                 
34827                 b.el.position('absolute');
34828                 
34829                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34830                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34831                 
34832                 if(b.size == 'md-left' || b.size == 'md-right'){
34833                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34834                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34835                 }
34836                 
34837                 b.el.setWidth(width);
34838                 b.el.setHeight(height);
34839                 // iframe?
34840                 b.el.select('iframe',true).setSize(width,height);
34841                 
34842             }, this);
34843             
34844             for (var i = 0; i < this.cols; i++){
34845                 
34846                 if(maxY[i] < maxY[col]){
34847                     col = i;
34848                     continue;
34849                 }
34850                 
34851                 col = Math.min(col, i);
34852                 
34853             }
34854             
34855             x = pos.x + col * (this.colWidth + this.padWidth);
34856             
34857             y = maxY[col];
34858             
34859             var positions = [];
34860             
34861             switch (box.length){
34862                 case 1 :
34863                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34864                     break;
34865                 case 2 :
34866                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34867                     break;
34868                 case 3 :
34869                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34870                     break;
34871                 case 4 :
34872                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34873                     break;
34874                 default :
34875                     break;
34876             }
34877             
34878             Roo.each(box, function(b,kk){
34879                 
34880                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34881                 
34882                 var sz = b.el.getSize();
34883                 
34884                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34885                 
34886             }, this);
34887             
34888         }, this);
34889         
34890         var mY = 0;
34891         
34892         for (var i = 0; i < this.cols; i++){
34893             mY = Math.max(mY, maxY[i]);
34894         }
34895         
34896         this.el.setHeight(mY - pos.y);
34897         
34898     },
34899     
34900 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34901 //    {
34902 //        var pos = this.el.getBox(true);
34903 //        var x = pos.x;
34904 //        var y = pos.y;
34905 //        var maxX = pos.right;
34906 //        
34907 //        var maxHeight = 0;
34908 //        
34909 //        Roo.each(items, function(item, k){
34910 //            
34911 //            var c = k % 2;
34912 //            
34913 //            item.el.position('absolute');
34914 //                
34915 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34916 //
34917 //            item.el.setWidth(width);
34918 //
34919 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34920 //
34921 //            item.el.setHeight(height);
34922 //            
34923 //            if(c == 0){
34924 //                item.el.setXY([x, y], isInstant ? false : true);
34925 //            } else {
34926 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34927 //            }
34928 //            
34929 //            y = y + height + this.alternativePadWidth;
34930 //            
34931 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34932 //            
34933 //        }, this);
34934 //        
34935 //        this.el.setHeight(maxHeight);
34936 //        
34937 //    },
34938     
34939     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34940     {
34941         var pos = this.el.getBox(true);
34942         
34943         var minX = pos.x;
34944         var minY = pos.y;
34945         
34946         var maxX = pos.right;
34947         
34948         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34949         
34950         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34951         
34952         Roo.each(queue, function(box, k){
34953             
34954             Roo.each(box, function(b, kk){
34955                 
34956                 b.el.position('absolute');
34957                 
34958                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34959                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34960                 
34961                 if(b.size == 'md-left' || b.size == 'md-right'){
34962                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34963                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34964                 }
34965                 
34966                 b.el.setWidth(width);
34967                 b.el.setHeight(height);
34968                 
34969             }, this);
34970             
34971             if(!box.length){
34972                 return;
34973             }
34974             
34975             var positions = [];
34976             
34977             switch (box.length){
34978                 case 1 :
34979                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34980                     break;
34981                 case 2 :
34982                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34983                     break;
34984                 case 3 :
34985                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34986                     break;
34987                 case 4 :
34988                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34989                     break;
34990                 default :
34991                     break;
34992             }
34993             
34994             Roo.each(box, function(b,kk){
34995                 
34996                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34997                 
34998                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34999                 
35000             }, this);
35001             
35002         }, this);
35003         
35004     },
35005     
35006     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35007     {
35008         Roo.each(eItems, function(b,k){
35009             
35010             b.size = (k == 0) ? 'sm' : 'xs';
35011             b.x = (k == 0) ? 2 : 1;
35012             b.y = (k == 0) ? 2 : 1;
35013             
35014             b.el.position('absolute');
35015             
35016             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35017                 
35018             b.el.setWidth(width);
35019             
35020             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35021             
35022             b.el.setHeight(height);
35023             
35024         }, this);
35025
35026         var positions = [];
35027         
35028         positions.push({
35029             x : maxX - this.unitWidth * 2 - this.gutter,
35030             y : minY
35031         });
35032         
35033         positions.push({
35034             x : maxX - this.unitWidth,
35035             y : minY + (this.unitWidth + this.gutter) * 2
35036         });
35037         
35038         positions.push({
35039             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35040             y : minY
35041         });
35042         
35043         Roo.each(eItems, function(b,k){
35044             
35045             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35046
35047         }, this);
35048         
35049     },
35050     
35051     getVerticalOneBoxColPositions : function(x, y, box)
35052     {
35053         var pos = [];
35054         
35055         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35056         
35057         if(box[0].size == 'md-left'){
35058             rand = 0;
35059         }
35060         
35061         if(box[0].size == 'md-right'){
35062             rand = 1;
35063         }
35064         
35065         pos.push({
35066             x : x + (this.unitWidth + this.gutter) * rand,
35067             y : y
35068         });
35069         
35070         return pos;
35071     },
35072     
35073     getVerticalTwoBoxColPositions : function(x, y, box)
35074     {
35075         var pos = [];
35076         
35077         if(box[0].size == 'xs'){
35078             
35079             pos.push({
35080                 x : x,
35081                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35082             });
35083
35084             pos.push({
35085                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35086                 y : y
35087             });
35088             
35089             return pos;
35090             
35091         }
35092         
35093         pos.push({
35094             x : x,
35095             y : y
35096         });
35097
35098         pos.push({
35099             x : x + (this.unitWidth + this.gutter) * 2,
35100             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35101         });
35102         
35103         return pos;
35104         
35105     },
35106     
35107     getVerticalThreeBoxColPositions : function(x, y, box)
35108     {
35109         var pos = [];
35110         
35111         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35112             
35113             pos.push({
35114                 x : x,
35115                 y : y
35116             });
35117
35118             pos.push({
35119                 x : x + (this.unitWidth + this.gutter) * 1,
35120                 y : y
35121             });
35122             
35123             pos.push({
35124                 x : x + (this.unitWidth + this.gutter) * 2,
35125                 y : y
35126             });
35127             
35128             return pos;
35129             
35130         }
35131         
35132         if(box[0].size == 'xs' && box[1].size == 'xs'){
35133             
35134             pos.push({
35135                 x : x,
35136                 y : y
35137             });
35138
35139             pos.push({
35140                 x : x,
35141                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35142             });
35143             
35144             pos.push({
35145                 x : x + (this.unitWidth + this.gutter) * 1,
35146                 y : y
35147             });
35148             
35149             return pos;
35150             
35151         }
35152         
35153         pos.push({
35154             x : x,
35155             y : y
35156         });
35157
35158         pos.push({
35159             x : x + (this.unitWidth + this.gutter) * 2,
35160             y : y
35161         });
35162
35163         pos.push({
35164             x : x + (this.unitWidth + this.gutter) * 2,
35165             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35166         });
35167             
35168         return pos;
35169         
35170     },
35171     
35172     getVerticalFourBoxColPositions : function(x, y, box)
35173     {
35174         var pos = [];
35175         
35176         if(box[0].size == 'xs'){
35177             
35178             pos.push({
35179                 x : x,
35180                 y : y
35181             });
35182
35183             pos.push({
35184                 x : x,
35185                 y : y + (this.unitHeight + this.gutter) * 1
35186             });
35187             
35188             pos.push({
35189                 x : x,
35190                 y : y + (this.unitHeight + this.gutter) * 2
35191             });
35192             
35193             pos.push({
35194                 x : x + (this.unitWidth + this.gutter) * 1,
35195                 y : y
35196             });
35197             
35198             return pos;
35199             
35200         }
35201         
35202         pos.push({
35203             x : x,
35204             y : y
35205         });
35206
35207         pos.push({
35208             x : x + (this.unitWidth + this.gutter) * 2,
35209             y : y
35210         });
35211
35212         pos.push({
35213             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35214             y : y + (this.unitHeight + this.gutter) * 1
35215         });
35216
35217         pos.push({
35218             x : x + (this.unitWidth + this.gutter) * 2,
35219             y : y + (this.unitWidth + this.gutter) * 2
35220         });
35221
35222         return pos;
35223         
35224     },
35225     
35226     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35227     {
35228         var pos = [];
35229         
35230         if(box[0].size == 'md-left'){
35231             pos.push({
35232                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35233                 y : minY
35234             });
35235             
35236             return pos;
35237         }
35238         
35239         if(box[0].size == 'md-right'){
35240             pos.push({
35241                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35242                 y : minY + (this.unitWidth + this.gutter) * 1
35243             });
35244             
35245             return pos;
35246         }
35247         
35248         var rand = Math.floor(Math.random() * (4 - box[0].y));
35249         
35250         pos.push({
35251             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35252             y : minY + (this.unitWidth + this.gutter) * rand
35253         });
35254         
35255         return pos;
35256         
35257     },
35258     
35259     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35260     {
35261         var pos = [];
35262         
35263         if(box[0].size == 'xs'){
35264             
35265             pos.push({
35266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35267                 y : minY
35268             });
35269
35270             pos.push({
35271                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35272                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35273             });
35274             
35275             return pos;
35276             
35277         }
35278         
35279         pos.push({
35280             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35281             y : minY
35282         });
35283
35284         pos.push({
35285             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35286             y : minY + (this.unitWidth + this.gutter) * 2
35287         });
35288         
35289         return pos;
35290         
35291     },
35292     
35293     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35294     {
35295         var pos = [];
35296         
35297         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35298             
35299             pos.push({
35300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35301                 y : minY
35302             });
35303
35304             pos.push({
35305                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35306                 y : minY + (this.unitWidth + this.gutter) * 1
35307             });
35308             
35309             pos.push({
35310                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35311                 y : minY + (this.unitWidth + this.gutter) * 2
35312             });
35313             
35314             return pos;
35315             
35316         }
35317         
35318         if(box[0].size == 'xs' && box[1].size == 'xs'){
35319             
35320             pos.push({
35321                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35322                 y : minY
35323             });
35324
35325             pos.push({
35326                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35327                 y : minY
35328             });
35329             
35330             pos.push({
35331                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35332                 y : minY + (this.unitWidth + this.gutter) * 1
35333             });
35334             
35335             return pos;
35336             
35337         }
35338         
35339         pos.push({
35340             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35341             y : minY
35342         });
35343
35344         pos.push({
35345             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35346             y : minY + (this.unitWidth + this.gutter) * 2
35347         });
35348
35349         pos.push({
35350             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35351             y : minY + (this.unitWidth + this.gutter) * 2
35352         });
35353             
35354         return pos;
35355         
35356     },
35357     
35358     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35359     {
35360         var pos = [];
35361         
35362         if(box[0].size == 'xs'){
35363             
35364             pos.push({
35365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35366                 y : minY
35367             });
35368
35369             pos.push({
35370                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35371                 y : minY
35372             });
35373             
35374             pos.push({
35375                 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),
35376                 y : minY
35377             });
35378             
35379             pos.push({
35380                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35381                 y : minY + (this.unitWidth + this.gutter) * 1
35382             });
35383             
35384             return pos;
35385             
35386         }
35387         
35388         pos.push({
35389             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35390             y : minY
35391         });
35392         
35393         pos.push({
35394             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35395             y : minY + (this.unitWidth + this.gutter) * 2
35396         });
35397         
35398         pos.push({
35399             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35400             y : minY + (this.unitWidth + this.gutter) * 2
35401         });
35402         
35403         pos.push({
35404             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),
35405             y : minY + (this.unitWidth + this.gutter) * 2
35406         });
35407
35408         return pos;
35409         
35410     },
35411     
35412     /**
35413     * remove a Masonry Brick
35414     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35415     */
35416     removeBrick : function(brick_id)
35417     {
35418         if (!brick_id) {
35419             return;
35420         }
35421         
35422         for (var i = 0; i<this.bricks.length; i++) {
35423             if (this.bricks[i].id == brick_id) {
35424                 this.bricks.splice(i,1);
35425                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35426                 this.initial();
35427             }
35428         }
35429     },
35430     
35431     /**
35432     * adds a Masonry Brick
35433     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35434     */
35435     addBrick : function(cfg)
35436     {
35437         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35438         //this.register(cn);
35439         cn.parentId = this.id;
35440         cn.render(this.el);
35441         return cn;
35442     },
35443     
35444     /**
35445     * register a Masonry Brick
35446     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35447     */
35448     
35449     register : function(brick)
35450     {
35451         this.bricks.push(brick);
35452         brick.masonryId = this.id;
35453     },
35454     
35455     /**
35456     * clear all the Masonry Brick
35457     */
35458     clearAll : function()
35459     {
35460         this.bricks = [];
35461         //this.getChildContainer().dom.innerHTML = "";
35462         this.el.dom.innerHTML = '';
35463     },
35464     
35465     getSelected : function()
35466     {
35467         if (!this.selectedBrick) {
35468             return false;
35469         }
35470         
35471         return this.selectedBrick;
35472     }
35473 });
35474
35475 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35476     
35477     groups: {},
35478      /**
35479     * register a Masonry Layout
35480     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35481     */
35482     
35483     register : function(layout)
35484     {
35485         this.groups[layout.id] = layout;
35486     },
35487     /**
35488     * fetch a  Masonry Layout based on the masonry layout ID
35489     * @param {string} the masonry layout to add
35490     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35491     */
35492     
35493     get: function(layout_id) {
35494         if (typeof(this.groups[layout_id]) == 'undefined') {
35495             return false;
35496         }
35497         return this.groups[layout_id] ;
35498     }
35499     
35500     
35501     
35502 });
35503
35504  
35505
35506  /**
35507  *
35508  * This is based on 
35509  * http://masonry.desandro.com
35510  *
35511  * The idea is to render all the bricks based on vertical width...
35512  *
35513  * The original code extends 'outlayer' - we might need to use that....
35514  * 
35515  */
35516
35517
35518 /**
35519  * @class Roo.bootstrap.LayoutMasonryAuto
35520  * @extends Roo.bootstrap.Component
35521  * Bootstrap Layout Masonry class
35522  * 
35523  * @constructor
35524  * Create a new Element
35525  * @param {Object} config The config object
35526  */
35527
35528 Roo.bootstrap.LayoutMasonryAuto = function(config){
35529     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35530 };
35531
35532 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35533     
35534       /**
35535      * @cfg {Boolean} isFitWidth  - resize the width..
35536      */   
35537     isFitWidth : false,  // options..
35538     /**
35539      * @cfg {Boolean} isOriginLeft = left align?
35540      */   
35541     isOriginLeft : true,
35542     /**
35543      * @cfg {Boolean} isOriginTop = top align?
35544      */   
35545     isOriginTop : false,
35546     /**
35547      * @cfg {Boolean} isLayoutInstant = no animation?
35548      */   
35549     isLayoutInstant : false, // needed?
35550     /**
35551      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35552      */   
35553     isResizingContainer : true,
35554     /**
35555      * @cfg {Number} columnWidth  width of the columns 
35556      */   
35557     
35558     columnWidth : 0,
35559     
35560     /**
35561      * @cfg {Number} maxCols maximum number of columns
35562      */   
35563     
35564     maxCols: 0,
35565     /**
35566      * @cfg {Number} padHeight padding below box..
35567      */   
35568     
35569     padHeight : 10, 
35570     
35571     /**
35572      * @cfg {Boolean} isAutoInitial defalut true
35573      */   
35574     
35575     isAutoInitial : true, 
35576     
35577     // private?
35578     gutter : 0,
35579     
35580     containerWidth: 0,
35581     initialColumnWidth : 0,
35582     currentSize : null,
35583     
35584     colYs : null, // array.
35585     maxY : 0,
35586     padWidth: 10,
35587     
35588     
35589     tag: 'div',
35590     cls: '',
35591     bricks: null, //CompositeElement
35592     cols : 0, // array?
35593     // element : null, // wrapped now this.el
35594     _isLayoutInited : null, 
35595     
35596     
35597     getAutoCreate : function(){
35598         
35599         var cfg = {
35600             tag: this.tag,
35601             cls: 'blog-masonary-wrapper ' + this.cls,
35602             cn : {
35603                 cls : 'mas-boxes masonary'
35604             }
35605         };
35606         
35607         return cfg;
35608     },
35609     
35610     getChildContainer: function( )
35611     {
35612         if (this.boxesEl) {
35613             return this.boxesEl;
35614         }
35615         
35616         this.boxesEl = this.el.select('.mas-boxes').first();
35617         
35618         return this.boxesEl;
35619     },
35620     
35621     
35622     initEvents : function()
35623     {
35624         var _this = this;
35625         
35626         if(this.isAutoInitial){
35627             Roo.log('hook children rendered');
35628             this.on('childrenrendered', function() {
35629                 Roo.log('children rendered');
35630                 _this.initial();
35631             } ,this);
35632         }
35633         
35634     },
35635     
35636     initial : function()
35637     {
35638         this.reloadItems();
35639
35640         this.currentSize = this.el.getBox(true);
35641
35642         /// was window resize... - let's see if this works..
35643         Roo.EventManager.onWindowResize(this.resize, this); 
35644
35645         if(!this.isAutoInitial){
35646             this.layout();
35647             return;
35648         }
35649         
35650         this.layout.defer(500,this);
35651     },
35652     
35653     reloadItems: function()
35654     {
35655         this.bricks = this.el.select('.masonry-brick', true);
35656         
35657         this.bricks.each(function(b) {
35658             //Roo.log(b.getSize());
35659             if (!b.attr('originalwidth')) {
35660                 b.attr('originalwidth',  b.getSize().width);
35661             }
35662             
35663         });
35664         
35665         Roo.log(this.bricks.elements.length);
35666     },
35667     
35668     resize : function()
35669     {
35670         Roo.log('resize');
35671         var cs = this.el.getBox(true);
35672         
35673         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35674             Roo.log("no change in with or X");
35675             return;
35676         }
35677         this.currentSize = cs;
35678         this.layout();
35679     },
35680     
35681     layout : function()
35682     {
35683          Roo.log('layout');
35684         this._resetLayout();
35685         //this._manageStamps();
35686       
35687         // don't animate first layout
35688         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35689         this.layoutItems( isInstant );
35690       
35691         // flag for initalized
35692         this._isLayoutInited = true;
35693     },
35694     
35695     layoutItems : function( isInstant )
35696     {
35697         //var items = this._getItemsForLayout( this.items );
35698         // original code supports filtering layout items.. we just ignore it..
35699         
35700         this._layoutItems( this.bricks , isInstant );
35701       
35702         this._postLayout();
35703     },
35704     _layoutItems : function ( items , isInstant)
35705     {
35706        //this.fireEvent( 'layout', this, items );
35707     
35708
35709         if ( !items || !items.elements.length ) {
35710           // no items, emit event with empty array
35711             return;
35712         }
35713
35714         var queue = [];
35715         items.each(function(item) {
35716             Roo.log("layout item");
35717             Roo.log(item);
35718             // get x/y object from method
35719             var position = this._getItemLayoutPosition( item );
35720             // enqueue
35721             position.item = item;
35722             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35723             queue.push( position );
35724         }, this);
35725       
35726         this._processLayoutQueue( queue );
35727     },
35728     /** Sets position of item in DOM
35729     * @param {Element} item
35730     * @param {Number} x - horizontal position
35731     * @param {Number} y - vertical position
35732     * @param {Boolean} isInstant - disables transitions
35733     */
35734     _processLayoutQueue : function( queue )
35735     {
35736         for ( var i=0, len = queue.length; i < len; i++ ) {
35737             var obj = queue[i];
35738             obj.item.position('absolute');
35739             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35740         }
35741     },
35742       
35743     
35744     /**
35745     * Any logic you want to do after each layout,
35746     * i.e. size the container
35747     */
35748     _postLayout : function()
35749     {
35750         this.resizeContainer();
35751     },
35752     
35753     resizeContainer : function()
35754     {
35755         if ( !this.isResizingContainer ) {
35756             return;
35757         }
35758         var size = this._getContainerSize();
35759         if ( size ) {
35760             this.el.setSize(size.width,size.height);
35761             this.boxesEl.setSize(size.width,size.height);
35762         }
35763     },
35764     
35765     
35766     
35767     _resetLayout : function()
35768     {
35769         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35770         this.colWidth = this.el.getWidth();
35771         //this.gutter = this.el.getWidth(); 
35772         
35773         this.measureColumns();
35774
35775         // reset column Y
35776         var i = this.cols;
35777         this.colYs = [];
35778         while (i--) {
35779             this.colYs.push( 0 );
35780         }
35781     
35782         this.maxY = 0;
35783     },
35784
35785     measureColumns : function()
35786     {
35787         this.getContainerWidth();
35788       // if columnWidth is 0, default to outerWidth of first item
35789         if ( !this.columnWidth ) {
35790             var firstItem = this.bricks.first();
35791             Roo.log(firstItem);
35792             this.columnWidth  = this.containerWidth;
35793             if (firstItem && firstItem.attr('originalwidth') ) {
35794                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35795             }
35796             // columnWidth fall back to item of first element
35797             Roo.log("set column width?");
35798                         this.initialColumnWidth = this.columnWidth  ;
35799
35800             // if first elem has no width, default to size of container
35801             
35802         }
35803         
35804         
35805         if (this.initialColumnWidth) {
35806             this.columnWidth = this.initialColumnWidth;
35807         }
35808         
35809         
35810             
35811         // column width is fixed at the top - however if container width get's smaller we should
35812         // reduce it...
35813         
35814         // this bit calcs how man columns..
35815             
35816         var columnWidth = this.columnWidth += this.gutter;
35817       
35818         // calculate columns
35819         var containerWidth = this.containerWidth + this.gutter;
35820         
35821         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35822         // fix rounding errors, typically with gutters
35823         var excess = columnWidth - containerWidth % columnWidth;
35824         
35825         
35826         // if overshoot is less than a pixel, round up, otherwise floor it
35827         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35828         cols = Math[ mathMethod ]( cols );
35829         this.cols = Math.max( cols, 1 );
35830         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35831         
35832          // padding positioning..
35833         var totalColWidth = this.cols * this.columnWidth;
35834         var padavail = this.containerWidth - totalColWidth;
35835         // so for 2 columns - we need 3 'pads'
35836         
35837         var padNeeded = (1+this.cols) * this.padWidth;
35838         
35839         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35840         
35841         this.columnWidth += padExtra
35842         //this.padWidth = Math.floor(padavail /  ( this.cols));
35843         
35844         // adjust colum width so that padding is fixed??
35845         
35846         // we have 3 columns ... total = width * 3
35847         // we have X left over... that should be used by 
35848         
35849         //if (this.expandC) {
35850             
35851         //}
35852         
35853         
35854         
35855     },
35856     
35857     getContainerWidth : function()
35858     {
35859        /* // container is parent if fit width
35860         var container = this.isFitWidth ? this.element.parentNode : this.element;
35861         // check that this.size and size are there
35862         // IE8 triggers resize on body size change, so they might not be
35863         
35864         var size = getSize( container );  //FIXME
35865         this.containerWidth = size && size.innerWidth; //FIXME
35866         */
35867          
35868         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35869         
35870     },
35871     
35872     _getItemLayoutPosition : function( item )  // what is item?
35873     {
35874         // we resize the item to our columnWidth..
35875       
35876         item.setWidth(this.columnWidth);
35877         item.autoBoxAdjust  = false;
35878         
35879         var sz = item.getSize();
35880  
35881         // how many columns does this brick span
35882         var remainder = this.containerWidth % this.columnWidth;
35883         
35884         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35885         // round if off by 1 pixel, otherwise use ceil
35886         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35887         colSpan = Math.min( colSpan, this.cols );
35888         
35889         // normally this should be '1' as we dont' currently allow multi width columns..
35890         
35891         var colGroup = this._getColGroup( colSpan );
35892         // get the minimum Y value from the columns
35893         var minimumY = Math.min.apply( Math, colGroup );
35894         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35895         
35896         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35897          
35898         // position the brick
35899         var position = {
35900             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35901             y: this.currentSize.y + minimumY + this.padHeight
35902         };
35903         
35904         Roo.log(position);
35905         // apply setHeight to necessary columns
35906         var setHeight = minimumY + sz.height + this.padHeight;
35907         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35908         
35909         var setSpan = this.cols + 1 - colGroup.length;
35910         for ( var i = 0; i < setSpan; i++ ) {
35911           this.colYs[ shortColIndex + i ] = setHeight ;
35912         }
35913       
35914         return position;
35915     },
35916     
35917     /**
35918      * @param {Number} colSpan - number of columns the element spans
35919      * @returns {Array} colGroup
35920      */
35921     _getColGroup : function( colSpan )
35922     {
35923         if ( colSpan < 2 ) {
35924           // if brick spans only one column, use all the column Ys
35925           return this.colYs;
35926         }
35927       
35928         var colGroup = [];
35929         // how many different places could this brick fit horizontally
35930         var groupCount = this.cols + 1 - colSpan;
35931         // for each group potential horizontal position
35932         for ( var i = 0; i < groupCount; i++ ) {
35933           // make an array of colY values for that one group
35934           var groupColYs = this.colYs.slice( i, i + colSpan );
35935           // and get the max value of the array
35936           colGroup[i] = Math.max.apply( Math, groupColYs );
35937         }
35938         return colGroup;
35939     },
35940     /*
35941     _manageStamp : function( stamp )
35942     {
35943         var stampSize =  stamp.getSize();
35944         var offset = stamp.getBox();
35945         // get the columns that this stamp affects
35946         var firstX = this.isOriginLeft ? offset.x : offset.right;
35947         var lastX = firstX + stampSize.width;
35948         var firstCol = Math.floor( firstX / this.columnWidth );
35949         firstCol = Math.max( 0, firstCol );
35950         
35951         var lastCol = Math.floor( lastX / this.columnWidth );
35952         // lastCol should not go over if multiple of columnWidth #425
35953         lastCol -= lastX % this.columnWidth ? 0 : 1;
35954         lastCol = Math.min( this.cols - 1, lastCol );
35955         
35956         // set colYs to bottom of the stamp
35957         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35958             stampSize.height;
35959             
35960         for ( var i = firstCol; i <= lastCol; i++ ) {
35961           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35962         }
35963     },
35964     */
35965     
35966     _getContainerSize : function()
35967     {
35968         this.maxY = Math.max.apply( Math, this.colYs );
35969         var size = {
35970             height: this.maxY
35971         };
35972       
35973         if ( this.isFitWidth ) {
35974             size.width = this._getContainerFitWidth();
35975         }
35976       
35977         return size;
35978     },
35979     
35980     _getContainerFitWidth : function()
35981     {
35982         var unusedCols = 0;
35983         // count unused columns
35984         var i = this.cols;
35985         while ( --i ) {
35986           if ( this.colYs[i] !== 0 ) {
35987             break;
35988           }
35989           unusedCols++;
35990         }
35991         // fit container to columns that have been used
35992         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35993     },
35994     
35995     needsResizeLayout : function()
35996     {
35997         var previousWidth = this.containerWidth;
35998         this.getContainerWidth();
35999         return previousWidth !== this.containerWidth;
36000     }
36001  
36002 });
36003
36004  
36005
36006  /*
36007  * - LGPL
36008  *
36009  * element
36010  * 
36011  */
36012
36013 /**
36014  * @class Roo.bootstrap.MasonryBrick
36015  * @extends Roo.bootstrap.Component
36016  * Bootstrap MasonryBrick class
36017  * 
36018  * @constructor
36019  * Create a new MasonryBrick
36020  * @param {Object} config The config object
36021  */
36022
36023 Roo.bootstrap.MasonryBrick = function(config){
36024     
36025     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36026     
36027     Roo.bootstrap.MasonryBrick.register(this);
36028     
36029     this.addEvents({
36030         // raw events
36031         /**
36032          * @event click
36033          * When a MasonryBrick is clcik
36034          * @param {Roo.bootstrap.MasonryBrick} this
36035          * @param {Roo.EventObject} e
36036          */
36037         "click" : true
36038     });
36039 };
36040
36041 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36042     
36043     /**
36044      * @cfg {String} title
36045      */   
36046     title : '',
36047     /**
36048      * @cfg {String} html
36049      */   
36050     html : '',
36051     /**
36052      * @cfg {String} bgimage
36053      */   
36054     bgimage : '',
36055     /**
36056      * @cfg {String} videourl
36057      */   
36058     videourl : '',
36059     /**
36060      * @cfg {String} cls
36061      */   
36062     cls : '',
36063     /**
36064      * @cfg {String} href
36065      */   
36066     href : '',
36067     /**
36068      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36069      */   
36070     size : 'xs',
36071     
36072     /**
36073      * @cfg {String} placetitle (center|bottom)
36074      */   
36075     placetitle : '',
36076     
36077     /**
36078      * @cfg {Boolean} isFitContainer defalut true
36079      */   
36080     isFitContainer : true, 
36081     
36082     /**
36083      * @cfg {Boolean} preventDefault defalut false
36084      */   
36085     preventDefault : false, 
36086     
36087     /**
36088      * @cfg {Boolean} inverse defalut false
36089      */   
36090     maskInverse : false, 
36091     
36092     getAutoCreate : function()
36093     {
36094         if(!this.isFitContainer){
36095             return this.getSplitAutoCreate();
36096         }
36097         
36098         var cls = 'masonry-brick masonry-brick-full';
36099         
36100         if(this.href.length){
36101             cls += ' masonry-brick-link';
36102         }
36103         
36104         if(this.bgimage.length){
36105             cls += ' masonry-brick-image';
36106         }
36107         
36108         if(this.maskInverse){
36109             cls += ' mask-inverse';
36110         }
36111         
36112         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36113             cls += ' enable-mask';
36114         }
36115         
36116         if(this.size){
36117             cls += ' masonry-' + this.size + '-brick';
36118         }
36119         
36120         if(this.placetitle.length){
36121             
36122             switch (this.placetitle) {
36123                 case 'center' :
36124                     cls += ' masonry-center-title';
36125                     break;
36126                 case 'bottom' :
36127                     cls += ' masonry-bottom-title';
36128                     break;
36129                 default:
36130                     break;
36131             }
36132             
36133         } else {
36134             if(!this.html.length && !this.bgimage.length){
36135                 cls += ' masonry-center-title';
36136             }
36137
36138             if(!this.html.length && this.bgimage.length){
36139                 cls += ' masonry-bottom-title';
36140             }
36141         }
36142         
36143         if(this.cls){
36144             cls += ' ' + this.cls;
36145         }
36146         
36147         var cfg = {
36148             tag: (this.href.length) ? 'a' : 'div',
36149             cls: cls,
36150             cn: [
36151                 {
36152                     tag: 'div',
36153                     cls: 'masonry-brick-mask'
36154                 },
36155                 {
36156                     tag: 'div',
36157                     cls: 'masonry-brick-paragraph',
36158                     cn: []
36159                 }
36160             ]
36161         };
36162         
36163         if(this.href.length){
36164             cfg.href = this.href;
36165         }
36166         
36167         var cn = cfg.cn[1].cn;
36168         
36169         if(this.title.length){
36170             cn.push({
36171                 tag: 'h4',
36172                 cls: 'masonry-brick-title',
36173                 html: this.title
36174             });
36175         }
36176         
36177         if(this.html.length){
36178             cn.push({
36179                 tag: 'p',
36180                 cls: 'masonry-brick-text',
36181                 html: this.html
36182             });
36183         }
36184         
36185         if (!this.title.length && !this.html.length) {
36186             cfg.cn[1].cls += ' hide';
36187         }
36188         
36189         if(this.bgimage.length){
36190             cfg.cn.push({
36191                 tag: 'img',
36192                 cls: 'masonry-brick-image-view',
36193                 src: this.bgimage
36194             });
36195         }
36196         
36197         if(this.videourl.length){
36198             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36199             // youtube support only?
36200             cfg.cn.push({
36201                 tag: 'iframe',
36202                 cls: 'masonry-brick-image-view',
36203                 src: vurl,
36204                 frameborder : 0,
36205                 allowfullscreen : true
36206             });
36207         }
36208         
36209         return cfg;
36210         
36211     },
36212     
36213     getSplitAutoCreate : function()
36214     {
36215         var cls = 'masonry-brick masonry-brick-split';
36216         
36217         if(this.href.length){
36218             cls += ' masonry-brick-link';
36219         }
36220         
36221         if(this.bgimage.length){
36222             cls += ' masonry-brick-image';
36223         }
36224         
36225         if(this.size){
36226             cls += ' masonry-' + this.size + '-brick';
36227         }
36228         
36229         switch (this.placetitle) {
36230             case 'center' :
36231                 cls += ' masonry-center-title';
36232                 break;
36233             case 'bottom' :
36234                 cls += ' masonry-bottom-title';
36235                 break;
36236             default:
36237                 if(!this.bgimage.length){
36238                     cls += ' masonry-center-title';
36239                 }
36240
36241                 if(this.bgimage.length){
36242                     cls += ' masonry-bottom-title';
36243                 }
36244                 break;
36245         }
36246         
36247         if(this.cls){
36248             cls += ' ' + this.cls;
36249         }
36250         
36251         var cfg = {
36252             tag: (this.href.length) ? 'a' : 'div',
36253             cls: cls,
36254             cn: [
36255                 {
36256                     tag: 'div',
36257                     cls: 'masonry-brick-split-head',
36258                     cn: [
36259                         {
36260                             tag: 'div',
36261                             cls: 'masonry-brick-paragraph',
36262                             cn: []
36263                         }
36264                     ]
36265                 },
36266                 {
36267                     tag: 'div',
36268                     cls: 'masonry-brick-split-body',
36269                     cn: []
36270                 }
36271             ]
36272         };
36273         
36274         if(this.href.length){
36275             cfg.href = this.href;
36276         }
36277         
36278         if(this.title.length){
36279             cfg.cn[0].cn[0].cn.push({
36280                 tag: 'h4',
36281                 cls: 'masonry-brick-title',
36282                 html: this.title
36283             });
36284         }
36285         
36286         if(this.html.length){
36287             cfg.cn[1].cn.push({
36288                 tag: 'p',
36289                 cls: 'masonry-brick-text',
36290                 html: this.html
36291             });
36292         }
36293
36294         if(this.bgimage.length){
36295             cfg.cn[0].cn.push({
36296                 tag: 'img',
36297                 cls: 'masonry-brick-image-view',
36298                 src: this.bgimage
36299             });
36300         }
36301         
36302         if(this.videourl.length){
36303             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36304             // youtube support only?
36305             cfg.cn[0].cn.cn.push({
36306                 tag: 'iframe',
36307                 cls: 'masonry-brick-image-view',
36308                 src: vurl,
36309                 frameborder : 0,
36310                 allowfullscreen : true
36311             });
36312         }
36313         
36314         return cfg;
36315     },
36316     
36317     initEvents: function() 
36318     {
36319         switch (this.size) {
36320             case 'xs' :
36321                 this.x = 1;
36322                 this.y = 1;
36323                 break;
36324             case 'sm' :
36325                 this.x = 2;
36326                 this.y = 2;
36327                 break;
36328             case 'md' :
36329             case 'md-left' :
36330             case 'md-right' :
36331                 this.x = 3;
36332                 this.y = 3;
36333                 break;
36334             case 'tall' :
36335                 this.x = 2;
36336                 this.y = 3;
36337                 break;
36338             case 'wide' :
36339                 this.x = 3;
36340                 this.y = 2;
36341                 break;
36342             case 'wide-thin' :
36343                 this.x = 3;
36344                 this.y = 1;
36345                 break;
36346                         
36347             default :
36348                 break;
36349         }
36350         
36351         if(Roo.isTouch){
36352             this.el.on('touchstart', this.onTouchStart, this);
36353             this.el.on('touchmove', this.onTouchMove, this);
36354             this.el.on('touchend', this.onTouchEnd, this);
36355             this.el.on('contextmenu', this.onContextMenu, this);
36356         } else {
36357             this.el.on('mouseenter'  ,this.enter, this);
36358             this.el.on('mouseleave', this.leave, this);
36359             this.el.on('click', this.onClick, this);
36360         }
36361         
36362         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36363             this.parent().bricks.push(this);   
36364         }
36365         
36366     },
36367     
36368     onClick: function(e, el)
36369     {
36370         var time = this.endTimer - this.startTimer;
36371         // Roo.log(e.preventDefault());
36372         if(Roo.isTouch){
36373             if(time > 1000){
36374                 e.preventDefault();
36375                 return;
36376             }
36377         }
36378         
36379         if(!this.preventDefault){
36380             return;
36381         }
36382         
36383         e.preventDefault();
36384         
36385         if (this.activeClass != '') {
36386             this.selectBrick();
36387         }
36388         
36389         this.fireEvent('click', this, e);
36390     },
36391     
36392     enter: function(e, el)
36393     {
36394         e.preventDefault();
36395         
36396         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36397             return;
36398         }
36399         
36400         if(this.bgimage.length && this.html.length){
36401             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36402         }
36403     },
36404     
36405     leave: function(e, el)
36406     {
36407         e.preventDefault();
36408         
36409         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36410             return;
36411         }
36412         
36413         if(this.bgimage.length && this.html.length){
36414             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36415         }
36416     },
36417     
36418     onTouchStart: function(e, el)
36419     {
36420 //        e.preventDefault();
36421         
36422         this.touchmoved = false;
36423         
36424         if(!this.isFitContainer){
36425             return;
36426         }
36427         
36428         if(!this.bgimage.length || !this.html.length){
36429             return;
36430         }
36431         
36432         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36433         
36434         this.timer = new Date().getTime();
36435         
36436     },
36437     
36438     onTouchMove: function(e, el)
36439     {
36440         this.touchmoved = true;
36441     },
36442     
36443     onContextMenu : function(e,el)
36444     {
36445         e.preventDefault();
36446         e.stopPropagation();
36447         return false;
36448     },
36449     
36450     onTouchEnd: function(e, el)
36451     {
36452 //        e.preventDefault();
36453         
36454         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36455         
36456             this.leave(e,el);
36457             
36458             return;
36459         }
36460         
36461         if(!this.bgimage.length || !this.html.length){
36462             
36463             if(this.href.length){
36464                 window.location.href = this.href;
36465             }
36466             
36467             return;
36468         }
36469         
36470         if(!this.isFitContainer){
36471             return;
36472         }
36473         
36474         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36475         
36476         window.location.href = this.href;
36477     },
36478     
36479     //selection on single brick only
36480     selectBrick : function() {
36481         
36482         if (!this.parentId) {
36483             return;
36484         }
36485         
36486         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36487         var index = m.selectedBrick.indexOf(this.id);
36488         
36489         if ( index > -1) {
36490             m.selectedBrick.splice(index,1);
36491             this.el.removeClass(this.activeClass);
36492             return;
36493         }
36494         
36495         for(var i = 0; i < m.selectedBrick.length; i++) {
36496             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36497             b.el.removeClass(b.activeClass);
36498         }
36499         
36500         m.selectedBrick = [];
36501         
36502         m.selectedBrick.push(this.id);
36503         this.el.addClass(this.activeClass);
36504         return;
36505     },
36506     
36507     isSelected : function(){
36508         return this.el.hasClass(this.activeClass);
36509         
36510     }
36511 });
36512
36513 Roo.apply(Roo.bootstrap.MasonryBrick, {
36514     
36515     //groups: {},
36516     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36517      /**
36518     * register a Masonry Brick
36519     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36520     */
36521     
36522     register : function(brick)
36523     {
36524         //this.groups[brick.id] = brick;
36525         this.groups.add(brick.id, brick);
36526     },
36527     /**
36528     * fetch a  masonry brick based on the masonry brick ID
36529     * @param {string} the masonry brick to add
36530     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36531     */
36532     
36533     get: function(brick_id) 
36534     {
36535         // if (typeof(this.groups[brick_id]) == 'undefined') {
36536         //     return false;
36537         // }
36538         // return this.groups[brick_id] ;
36539         
36540         if(this.groups.key(brick_id)) {
36541             return this.groups.key(brick_id);
36542         }
36543         
36544         return false;
36545     }
36546     
36547     
36548     
36549 });
36550
36551  /*
36552  * - LGPL
36553  *
36554  * element
36555  * 
36556  */
36557
36558 /**
36559  * @class Roo.bootstrap.Brick
36560  * @extends Roo.bootstrap.Component
36561  * Bootstrap Brick class
36562  * 
36563  * @constructor
36564  * Create a new Brick
36565  * @param {Object} config The config object
36566  */
36567
36568 Roo.bootstrap.Brick = function(config){
36569     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36570     
36571     this.addEvents({
36572         // raw events
36573         /**
36574          * @event click
36575          * When a Brick is click
36576          * @param {Roo.bootstrap.Brick} this
36577          * @param {Roo.EventObject} e
36578          */
36579         "click" : true
36580     });
36581 };
36582
36583 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36584     
36585     /**
36586      * @cfg {String} title
36587      */   
36588     title : '',
36589     /**
36590      * @cfg {String} html
36591      */   
36592     html : '',
36593     /**
36594      * @cfg {String} bgimage
36595      */   
36596     bgimage : '',
36597     /**
36598      * @cfg {String} cls
36599      */   
36600     cls : '',
36601     /**
36602      * @cfg {String} href
36603      */   
36604     href : '',
36605     /**
36606      * @cfg {String} video
36607      */   
36608     video : '',
36609     /**
36610      * @cfg {Boolean} square
36611      */   
36612     square : true,
36613     
36614     getAutoCreate : function()
36615     {
36616         var cls = 'roo-brick';
36617         
36618         if(this.href.length){
36619             cls += ' roo-brick-link';
36620         }
36621         
36622         if(this.bgimage.length){
36623             cls += ' roo-brick-image';
36624         }
36625         
36626         if(!this.html.length && !this.bgimage.length){
36627             cls += ' roo-brick-center-title';
36628         }
36629         
36630         if(!this.html.length && this.bgimage.length){
36631             cls += ' roo-brick-bottom-title';
36632         }
36633         
36634         if(this.cls){
36635             cls += ' ' + this.cls;
36636         }
36637         
36638         var cfg = {
36639             tag: (this.href.length) ? 'a' : 'div',
36640             cls: cls,
36641             cn: [
36642                 {
36643                     tag: 'div',
36644                     cls: 'roo-brick-paragraph',
36645                     cn: []
36646                 }
36647             ]
36648         };
36649         
36650         if(this.href.length){
36651             cfg.href = this.href;
36652         }
36653         
36654         var cn = cfg.cn[0].cn;
36655         
36656         if(this.title.length){
36657             cn.push({
36658                 tag: 'h4',
36659                 cls: 'roo-brick-title',
36660                 html: this.title
36661             });
36662         }
36663         
36664         if(this.html.length){
36665             cn.push({
36666                 tag: 'p',
36667                 cls: 'roo-brick-text',
36668                 html: this.html
36669             });
36670         } else {
36671             cn.cls += ' hide';
36672         }
36673         
36674         if(this.bgimage.length){
36675             cfg.cn.push({
36676                 tag: 'img',
36677                 cls: 'roo-brick-image-view',
36678                 src: this.bgimage
36679             });
36680         }
36681         
36682         return cfg;
36683     },
36684     
36685     initEvents: function() 
36686     {
36687         if(this.title.length || this.html.length){
36688             this.el.on('mouseenter'  ,this.enter, this);
36689             this.el.on('mouseleave', this.leave, this);
36690         }
36691         
36692         Roo.EventManager.onWindowResize(this.resize, this); 
36693         
36694         if(this.bgimage.length){
36695             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36696             this.imageEl.on('load', this.onImageLoad, this);
36697             return;
36698         }
36699         
36700         this.resize();
36701     },
36702     
36703     onImageLoad : function()
36704     {
36705         this.resize();
36706     },
36707     
36708     resize : function()
36709     {
36710         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36711         
36712         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36713         
36714         if(this.bgimage.length){
36715             var image = this.el.select('.roo-brick-image-view', true).first();
36716             
36717             image.setWidth(paragraph.getWidth());
36718             
36719             if(this.square){
36720                 image.setHeight(paragraph.getWidth());
36721             }
36722             
36723             this.el.setHeight(image.getHeight());
36724             paragraph.setHeight(image.getHeight());
36725             
36726         }
36727         
36728     },
36729     
36730     enter: function(e, el)
36731     {
36732         e.preventDefault();
36733         
36734         if(this.bgimage.length){
36735             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36736             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36737         }
36738     },
36739     
36740     leave: function(e, el)
36741     {
36742         e.preventDefault();
36743         
36744         if(this.bgimage.length){
36745             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36746             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36747         }
36748     }
36749     
36750 });
36751
36752  
36753
36754  /*
36755  * - LGPL
36756  *
36757  * Number field 
36758  */
36759
36760 /**
36761  * @class Roo.bootstrap.NumberField
36762  * @extends Roo.bootstrap.Input
36763  * Bootstrap NumberField class
36764  * 
36765  * 
36766  * 
36767  * 
36768  * @constructor
36769  * Create a new NumberField
36770  * @param {Object} config The config object
36771  */
36772
36773 Roo.bootstrap.NumberField = function(config){
36774     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36775 };
36776
36777 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36778     
36779     /**
36780      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36781      */
36782     allowDecimals : true,
36783     /**
36784      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36785      */
36786     decimalSeparator : ".",
36787     /**
36788      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36789      */
36790     decimalPrecision : 2,
36791     /**
36792      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36793      */
36794     allowNegative : true,
36795     
36796     /**
36797      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36798      */
36799     allowZero: true,
36800     /**
36801      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36802      */
36803     minValue : Number.NEGATIVE_INFINITY,
36804     /**
36805      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36806      */
36807     maxValue : Number.MAX_VALUE,
36808     /**
36809      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36810      */
36811     minText : "The minimum value for this field is {0}",
36812     /**
36813      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36814      */
36815     maxText : "The maximum value for this field is {0}",
36816     /**
36817      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36818      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36819      */
36820     nanText : "{0} is not a valid number",
36821     /**
36822      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36823      */
36824     thousandsDelimiter : false,
36825     /**
36826      * @cfg {String} valueAlign alignment of value
36827      */
36828     valueAlign : "left",
36829
36830     getAutoCreate : function()
36831     {
36832         var hiddenInput = {
36833             tag: 'input',
36834             type: 'hidden',
36835             id: Roo.id(),
36836             cls: 'hidden-number-input'
36837         };
36838         
36839         if (this.name) {
36840             hiddenInput.name = this.name;
36841         }
36842         
36843         this.name = '';
36844         
36845         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36846         
36847         this.name = hiddenInput.name;
36848         
36849         if(cfg.cn.length > 0) {
36850             cfg.cn.push(hiddenInput);
36851         }
36852         
36853         return cfg;
36854     },
36855
36856     // private
36857     initEvents : function()
36858     {   
36859         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36860         
36861         var allowed = "0123456789";
36862         
36863         if(this.allowDecimals){
36864             allowed += this.decimalSeparator;
36865         }
36866         
36867         if(this.allowNegative){
36868             allowed += "-";
36869         }
36870         
36871         if(this.thousandsDelimiter) {
36872             allowed += ",";
36873         }
36874         
36875         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36876         
36877         var keyPress = function(e){
36878             
36879             var k = e.getKey();
36880             
36881             var c = e.getCharCode();
36882             
36883             if(
36884                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36885                     allowed.indexOf(String.fromCharCode(c)) === -1
36886             ){
36887                 e.stopEvent();
36888                 return;
36889             }
36890             
36891             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36892                 return;
36893             }
36894             
36895             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36896                 e.stopEvent();
36897             }
36898         };
36899         
36900         this.el.on("keypress", keyPress, this);
36901     },
36902     
36903     validateValue : function(value)
36904     {
36905         
36906         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36907             return false;
36908         }
36909         
36910         var num = this.parseValue(value);
36911         
36912         if(isNaN(num)){
36913             this.markInvalid(String.format(this.nanText, value));
36914             return false;
36915         }
36916         
36917         if(num < this.minValue){
36918             this.markInvalid(String.format(this.minText, this.minValue));
36919             return false;
36920         }
36921         
36922         if(num > this.maxValue){
36923             this.markInvalid(String.format(this.maxText, this.maxValue));
36924             return false;
36925         }
36926         
36927         return true;
36928     },
36929
36930     getValue : function()
36931     {
36932         var v = this.hiddenEl().getValue();
36933         
36934         return this.fixPrecision(this.parseValue(v));
36935     },
36936
36937     parseValue : function(value)
36938     {
36939         if(this.thousandsDelimiter) {
36940             value += "";
36941             r = new RegExp(",", "g");
36942             value = value.replace(r, "");
36943         }
36944         
36945         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36946         return isNaN(value) ? '' : value;
36947     },
36948
36949     fixPrecision : function(value)
36950     {
36951         if(this.thousandsDelimiter) {
36952             value += "";
36953             r = new RegExp(",", "g");
36954             value = value.replace(r, "");
36955         }
36956         
36957         var nan = isNaN(value);
36958         
36959         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36960             return nan ? '' : value;
36961         }
36962         return parseFloat(value).toFixed(this.decimalPrecision);
36963     },
36964
36965     setValue : function(v)
36966     {
36967         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36968         
36969         this.value = v;
36970         
36971         if(this.rendered){
36972             
36973             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36974             
36975             this.inputEl().dom.value = (v == '') ? '' :
36976                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36977             
36978             if(!this.allowZero && v === '0') {
36979                 this.hiddenEl().dom.value = '';
36980                 this.inputEl().dom.value = '';
36981             }
36982             
36983             this.validate();
36984         }
36985     },
36986
36987     decimalPrecisionFcn : function(v)
36988     {
36989         return Math.floor(v);
36990     },
36991
36992     beforeBlur : function()
36993     {
36994         var v = this.parseValue(this.getRawValue());
36995         
36996         if(v || v === 0 || v === ''){
36997             this.setValue(v);
36998         }
36999     },
37000     
37001     hiddenEl : function()
37002     {
37003         return this.el.select('input.hidden-number-input',true).first();
37004     }
37005     
37006 });
37007
37008  
37009
37010 /*
37011 * Licence: LGPL
37012 */
37013
37014 /**
37015  * @class Roo.bootstrap.DocumentSlider
37016  * @extends Roo.bootstrap.Component
37017  * Bootstrap DocumentSlider class
37018  * 
37019  * @constructor
37020  * Create a new DocumentViewer
37021  * @param {Object} config The config object
37022  */
37023
37024 Roo.bootstrap.DocumentSlider = function(config){
37025     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37026     
37027     this.files = [];
37028     
37029     this.addEvents({
37030         /**
37031          * @event initial
37032          * Fire after initEvent
37033          * @param {Roo.bootstrap.DocumentSlider} this
37034          */
37035         "initial" : true,
37036         /**
37037          * @event update
37038          * Fire after update
37039          * @param {Roo.bootstrap.DocumentSlider} this
37040          */
37041         "update" : true,
37042         /**
37043          * @event click
37044          * Fire after click
37045          * @param {Roo.bootstrap.DocumentSlider} this
37046          */
37047         "click" : true
37048     });
37049 };
37050
37051 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37052     
37053     files : false,
37054     
37055     indicator : 0,
37056     
37057     getAutoCreate : function()
37058     {
37059         var cfg = {
37060             tag : 'div',
37061             cls : 'roo-document-slider',
37062             cn : [
37063                 {
37064                     tag : 'div',
37065                     cls : 'roo-document-slider-header',
37066                     cn : [
37067                         {
37068                             tag : 'div',
37069                             cls : 'roo-document-slider-header-title'
37070                         }
37071                     ]
37072                 },
37073                 {
37074                     tag : 'div',
37075                     cls : 'roo-document-slider-body',
37076                     cn : [
37077                         {
37078                             tag : 'div',
37079                             cls : 'roo-document-slider-prev',
37080                             cn : [
37081                                 {
37082                                     tag : 'i',
37083                                     cls : 'fa fa-chevron-left'
37084                                 }
37085                             ]
37086                         },
37087                         {
37088                             tag : 'div',
37089                             cls : 'roo-document-slider-thumb',
37090                             cn : [
37091                                 {
37092                                     tag : 'img',
37093                                     cls : 'roo-document-slider-image'
37094                                 }
37095                             ]
37096                         },
37097                         {
37098                             tag : 'div',
37099                             cls : 'roo-document-slider-next',
37100                             cn : [
37101                                 {
37102                                     tag : 'i',
37103                                     cls : 'fa fa-chevron-right'
37104                                 }
37105                             ]
37106                         }
37107                     ]
37108                 }
37109             ]
37110         };
37111         
37112         return cfg;
37113     },
37114     
37115     initEvents : function()
37116     {
37117         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37118         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37119         
37120         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37121         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37122         
37123         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37124         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37125         
37126         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37127         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37128         
37129         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37130         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37131         
37132         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37133         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37134         
37135         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37136         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37137         
37138         this.thumbEl.on('click', this.onClick, this);
37139         
37140         this.prevIndicator.on('click', this.prev, this);
37141         
37142         this.nextIndicator.on('click', this.next, this);
37143         
37144     },
37145     
37146     initial : function()
37147     {
37148         if(this.files.length){
37149             this.indicator = 1;
37150             this.update()
37151         }
37152         
37153         this.fireEvent('initial', this);
37154     },
37155     
37156     update : function()
37157     {
37158         this.imageEl.attr('src', this.files[this.indicator - 1]);
37159         
37160         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37161         
37162         this.prevIndicator.show();
37163         
37164         if(this.indicator == 1){
37165             this.prevIndicator.hide();
37166         }
37167         
37168         this.nextIndicator.show();
37169         
37170         if(this.indicator == this.files.length){
37171             this.nextIndicator.hide();
37172         }
37173         
37174         this.thumbEl.scrollTo('top');
37175         
37176         this.fireEvent('update', this);
37177     },
37178     
37179     onClick : function(e)
37180     {
37181         e.preventDefault();
37182         
37183         this.fireEvent('click', this);
37184     },
37185     
37186     prev : function(e)
37187     {
37188         e.preventDefault();
37189         
37190         this.indicator = Math.max(1, this.indicator - 1);
37191         
37192         this.update();
37193     },
37194     
37195     next : function(e)
37196     {
37197         e.preventDefault();
37198         
37199         this.indicator = Math.min(this.files.length, this.indicator + 1);
37200         
37201         this.update();
37202     }
37203 });
37204 /*
37205  * - LGPL
37206  *
37207  * RadioSet
37208  *
37209  *
37210  */
37211
37212 /**
37213  * @class Roo.bootstrap.RadioSet
37214  * @extends Roo.bootstrap.Input
37215  * Bootstrap RadioSet class
37216  * @cfg {String} indicatorpos (left|right) default left
37217  * @cfg {Boolean} inline (true|false) inline the element (default true)
37218  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37219  * @constructor
37220  * Create a new RadioSet
37221  * @param {Object} config The config object
37222  */
37223
37224 Roo.bootstrap.RadioSet = function(config){
37225     
37226     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37227     
37228     this.radioes = [];
37229     
37230     Roo.bootstrap.RadioSet.register(this);
37231     
37232     this.addEvents({
37233         /**
37234         * @event check
37235         * Fires when the element is checked or unchecked.
37236         * @param {Roo.bootstrap.RadioSet} this This radio
37237         * @param {Roo.bootstrap.Radio} item The checked item
37238         */
37239        check : true,
37240        /**
37241         * @event click
37242         * Fires when the element is click.
37243         * @param {Roo.bootstrap.RadioSet} this This radio set
37244         * @param {Roo.bootstrap.Radio} item The checked item
37245         * @param {Roo.EventObject} e The event object
37246         */
37247        click : true
37248     });
37249     
37250 };
37251
37252 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37253
37254     radioes : false,
37255     
37256     inline : true,
37257     
37258     weight : '',
37259     
37260     indicatorpos : 'left',
37261     
37262     getAutoCreate : function()
37263     {
37264         var label = {
37265             tag : 'label',
37266             cls : 'roo-radio-set-label',
37267             cn : [
37268                 {
37269                     tag : 'span',
37270                     html : this.fieldLabel
37271                 }
37272             ]
37273         };
37274         if (Roo.bootstrap.version == 3) {
37275             
37276             
37277             if(this.indicatorpos == 'left'){
37278                 label.cn.unshift({
37279                     tag : 'i',
37280                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37281                     tooltip : 'This field is required'
37282                 });
37283             } else {
37284                 label.cn.push({
37285                     tag : 'i',
37286                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37287                     tooltip : 'This field is required'
37288                 });
37289             }
37290         }
37291         var items = {
37292             tag : 'div',
37293             cls : 'roo-radio-set-items'
37294         };
37295         
37296         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37297         
37298         if (align === 'left' && this.fieldLabel.length) {
37299             
37300             items = {
37301                 cls : "roo-radio-set-right", 
37302                 cn: [
37303                     items
37304                 ]
37305             };
37306             
37307             if(this.labelWidth > 12){
37308                 label.style = "width: " + this.labelWidth + 'px';
37309             }
37310             
37311             if(this.labelWidth < 13 && this.labelmd == 0){
37312                 this.labelmd = this.labelWidth;
37313             }
37314             
37315             if(this.labellg > 0){
37316                 label.cls += ' col-lg-' + this.labellg;
37317                 items.cls += ' col-lg-' + (12 - this.labellg);
37318             }
37319             
37320             if(this.labelmd > 0){
37321                 label.cls += ' col-md-' + this.labelmd;
37322                 items.cls += ' col-md-' + (12 - this.labelmd);
37323             }
37324             
37325             if(this.labelsm > 0){
37326                 label.cls += ' col-sm-' + this.labelsm;
37327                 items.cls += ' col-sm-' + (12 - this.labelsm);
37328             }
37329             
37330             if(this.labelxs > 0){
37331                 label.cls += ' col-xs-' + this.labelxs;
37332                 items.cls += ' col-xs-' + (12 - this.labelxs);
37333             }
37334         }
37335         
37336         var cfg = {
37337             tag : 'div',
37338             cls : 'roo-radio-set',
37339             cn : [
37340                 {
37341                     tag : 'input',
37342                     cls : 'roo-radio-set-input',
37343                     type : 'hidden',
37344                     name : this.name,
37345                     value : this.value ? this.value :  ''
37346                 },
37347                 label,
37348                 items
37349             ]
37350         };
37351         
37352         if(this.weight.length){
37353             cfg.cls += ' roo-radio-' + this.weight;
37354         }
37355         
37356         if(this.inline) {
37357             cfg.cls += ' roo-radio-set-inline';
37358         }
37359         
37360         var settings=this;
37361         ['xs','sm','md','lg'].map(function(size){
37362             if (settings[size]) {
37363                 cfg.cls += ' col-' + size + '-' + settings[size];
37364             }
37365         });
37366         
37367         return cfg;
37368         
37369     },
37370
37371     initEvents : function()
37372     {
37373         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37374         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37375         
37376         if(!this.fieldLabel.length){
37377             this.labelEl.hide();
37378         }
37379         
37380         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37381         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37382         
37383         this.indicator = this.indicatorEl();
37384         
37385         if(this.indicator){
37386             this.indicator.addClass('invisible');
37387         }
37388         
37389         this.originalValue = this.getValue();
37390         
37391     },
37392     
37393     inputEl: function ()
37394     {
37395         return this.el.select('.roo-radio-set-input', true).first();
37396     },
37397     
37398     getChildContainer : function()
37399     {
37400         return this.itemsEl;
37401     },
37402     
37403     register : function(item)
37404     {
37405         this.radioes.push(item);
37406         
37407     },
37408     
37409     validate : function()
37410     {   
37411         if(this.getVisibilityEl().hasClass('hidden')){
37412             return true;
37413         }
37414         
37415         var valid = false;
37416         
37417         Roo.each(this.radioes, function(i){
37418             if(!i.checked){
37419                 return;
37420             }
37421             
37422             valid = true;
37423             return false;
37424         });
37425         
37426         if(this.allowBlank) {
37427             return true;
37428         }
37429         
37430         if(this.disabled || valid){
37431             this.markValid();
37432             return true;
37433         }
37434         
37435         this.markInvalid();
37436         return false;
37437         
37438     },
37439     
37440     markValid : function()
37441     {
37442         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37443             this.indicatorEl().removeClass('visible');
37444             this.indicatorEl().addClass('invisible');
37445         }
37446         
37447         
37448         if (Roo.bootstrap.version == 3) {
37449             this.el.removeClass([this.invalidClass, this.validClass]);
37450             this.el.addClass(this.validClass);
37451         } else {
37452             this.el.removeClass(['is-invalid','is-valid']);
37453             this.el.addClass(['is-valid']);
37454         }
37455         this.fireEvent('valid', this);
37456     },
37457     
37458     markInvalid : function(msg)
37459     {
37460         if(this.allowBlank || this.disabled){
37461             return;
37462         }
37463         
37464         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37465             this.indicatorEl().removeClass('invisible');
37466             this.indicatorEl().addClass('visible');
37467         }
37468         if (Roo.bootstrap.version == 3) {
37469             this.el.removeClass([this.invalidClass, this.validClass]);
37470             this.el.addClass(this.invalidClass);
37471         } else {
37472             this.el.removeClass(['is-invalid','is-valid']);
37473             this.el.addClass(['is-invalid']);
37474         }
37475         
37476         this.fireEvent('invalid', this, msg);
37477         
37478     },
37479     
37480     setValue : function(v, suppressEvent)
37481     {   
37482         if(this.value === v){
37483             return;
37484         }
37485         
37486         this.value = v;
37487         
37488         if(this.rendered){
37489             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37490         }
37491         
37492         Roo.each(this.radioes, function(i){
37493             i.checked = false;
37494             i.el.removeClass('checked');
37495         });
37496         
37497         Roo.each(this.radioes, function(i){
37498             
37499             if(i.value === v || i.value.toString() === v.toString()){
37500                 i.checked = true;
37501                 i.el.addClass('checked');
37502                 
37503                 if(suppressEvent !== true){
37504                     this.fireEvent('check', this, i);
37505                 }
37506                 
37507                 return false;
37508             }
37509             
37510         }, this);
37511         
37512         this.validate();
37513     },
37514     
37515     clearInvalid : function(){
37516         
37517         if(!this.el || this.preventMark){
37518             return;
37519         }
37520         
37521         this.el.removeClass([this.invalidClass]);
37522         
37523         this.fireEvent('valid', this);
37524     }
37525     
37526 });
37527
37528 Roo.apply(Roo.bootstrap.RadioSet, {
37529     
37530     groups: {},
37531     
37532     register : function(set)
37533     {
37534         this.groups[set.name] = set;
37535     },
37536     
37537     get: function(name) 
37538     {
37539         if (typeof(this.groups[name]) == 'undefined') {
37540             return false;
37541         }
37542         
37543         return this.groups[name] ;
37544     }
37545     
37546 });
37547 /*
37548  * Based on:
37549  * Ext JS Library 1.1.1
37550  * Copyright(c) 2006-2007, Ext JS, LLC.
37551  *
37552  * Originally Released Under LGPL - original licence link has changed is not relivant.
37553  *
37554  * Fork - LGPL
37555  * <script type="text/javascript">
37556  */
37557
37558
37559 /**
37560  * @class Roo.bootstrap.SplitBar
37561  * @extends Roo.util.Observable
37562  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37563  * <br><br>
37564  * Usage:
37565  * <pre><code>
37566 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37567                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37568 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37569 split.minSize = 100;
37570 split.maxSize = 600;
37571 split.animate = true;
37572 split.on('moved', splitterMoved);
37573 </code></pre>
37574  * @constructor
37575  * Create a new SplitBar
37576  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37577  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37578  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37579  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37580                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37581                         position of the SplitBar).
37582  */
37583 Roo.bootstrap.SplitBar = function(cfg){
37584     
37585     /** @private */
37586     
37587     //{
37588     //  dragElement : elm
37589     //  resizingElement: el,
37590         // optional..
37591     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37592     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37593         // existingProxy ???
37594     //}
37595     
37596     this.el = Roo.get(cfg.dragElement, true);
37597     this.el.dom.unselectable = "on";
37598     /** @private */
37599     this.resizingEl = Roo.get(cfg.resizingElement, true);
37600
37601     /**
37602      * @private
37603      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37604      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37605      * @type Number
37606      */
37607     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37608     
37609     /**
37610      * The minimum size of the resizing element. (Defaults to 0)
37611      * @type Number
37612      */
37613     this.minSize = 0;
37614     
37615     /**
37616      * The maximum size of the resizing element. (Defaults to 2000)
37617      * @type Number
37618      */
37619     this.maxSize = 2000;
37620     
37621     /**
37622      * Whether to animate the transition to the new size
37623      * @type Boolean
37624      */
37625     this.animate = false;
37626     
37627     /**
37628      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37629      * @type Boolean
37630      */
37631     this.useShim = false;
37632     
37633     /** @private */
37634     this.shim = null;
37635     
37636     if(!cfg.existingProxy){
37637         /** @private */
37638         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37639     }else{
37640         this.proxy = Roo.get(cfg.existingProxy).dom;
37641     }
37642     /** @private */
37643     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37644     
37645     /** @private */
37646     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37647     
37648     /** @private */
37649     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37650     
37651     /** @private */
37652     this.dragSpecs = {};
37653     
37654     /**
37655      * @private The adapter to use to positon and resize elements
37656      */
37657     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37658     this.adapter.init(this);
37659     
37660     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37661         /** @private */
37662         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37663         this.el.addClass("roo-splitbar-h");
37664     }else{
37665         /** @private */
37666         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37667         this.el.addClass("roo-splitbar-v");
37668     }
37669     
37670     this.addEvents({
37671         /**
37672          * @event resize
37673          * Fires when the splitter is moved (alias for {@link #event-moved})
37674          * @param {Roo.bootstrap.SplitBar} this
37675          * @param {Number} newSize the new width or height
37676          */
37677         "resize" : true,
37678         /**
37679          * @event moved
37680          * Fires when the splitter is moved
37681          * @param {Roo.bootstrap.SplitBar} this
37682          * @param {Number} newSize the new width or height
37683          */
37684         "moved" : true,
37685         /**
37686          * @event beforeresize
37687          * Fires before the splitter is dragged
37688          * @param {Roo.bootstrap.SplitBar} this
37689          */
37690         "beforeresize" : true,
37691
37692         "beforeapply" : true
37693     });
37694
37695     Roo.util.Observable.call(this);
37696 };
37697
37698 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37699     onStartProxyDrag : function(x, y){
37700         this.fireEvent("beforeresize", this);
37701         if(!this.overlay){
37702             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37703             o.unselectable();
37704             o.enableDisplayMode("block");
37705             // all splitbars share the same overlay
37706             Roo.bootstrap.SplitBar.prototype.overlay = o;
37707         }
37708         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37709         this.overlay.show();
37710         Roo.get(this.proxy).setDisplayed("block");
37711         var size = this.adapter.getElementSize(this);
37712         this.activeMinSize = this.getMinimumSize();;
37713         this.activeMaxSize = this.getMaximumSize();;
37714         var c1 = size - this.activeMinSize;
37715         var c2 = Math.max(this.activeMaxSize - size, 0);
37716         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37717             this.dd.resetConstraints();
37718             this.dd.setXConstraint(
37719                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37720                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37721             );
37722             this.dd.setYConstraint(0, 0);
37723         }else{
37724             this.dd.resetConstraints();
37725             this.dd.setXConstraint(0, 0);
37726             this.dd.setYConstraint(
37727                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37728                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37729             );
37730          }
37731         this.dragSpecs.startSize = size;
37732         this.dragSpecs.startPoint = [x, y];
37733         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37734     },
37735     
37736     /** 
37737      * @private Called after the drag operation by the DDProxy
37738      */
37739     onEndProxyDrag : function(e){
37740         Roo.get(this.proxy).setDisplayed(false);
37741         var endPoint = Roo.lib.Event.getXY(e);
37742         if(this.overlay){
37743             this.overlay.hide();
37744         }
37745         var newSize;
37746         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37747             newSize = this.dragSpecs.startSize + 
37748                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37749                     endPoint[0] - this.dragSpecs.startPoint[0] :
37750                     this.dragSpecs.startPoint[0] - endPoint[0]
37751                 );
37752         }else{
37753             newSize = this.dragSpecs.startSize + 
37754                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37755                     endPoint[1] - this.dragSpecs.startPoint[1] :
37756                     this.dragSpecs.startPoint[1] - endPoint[1]
37757                 );
37758         }
37759         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37760         if(newSize != this.dragSpecs.startSize){
37761             if(this.fireEvent('beforeapply', this, newSize) !== false){
37762                 this.adapter.setElementSize(this, newSize);
37763                 this.fireEvent("moved", this, newSize);
37764                 this.fireEvent("resize", this, newSize);
37765             }
37766         }
37767     },
37768     
37769     /**
37770      * Get the adapter this SplitBar uses
37771      * @return The adapter object
37772      */
37773     getAdapter : function(){
37774         return this.adapter;
37775     },
37776     
37777     /**
37778      * Set the adapter this SplitBar uses
37779      * @param {Object} adapter A SplitBar adapter object
37780      */
37781     setAdapter : function(adapter){
37782         this.adapter = adapter;
37783         this.adapter.init(this);
37784     },
37785     
37786     /**
37787      * Gets the minimum size for the resizing element
37788      * @return {Number} The minimum size
37789      */
37790     getMinimumSize : function(){
37791         return this.minSize;
37792     },
37793     
37794     /**
37795      * Sets the minimum size for the resizing element
37796      * @param {Number} minSize The minimum size
37797      */
37798     setMinimumSize : function(minSize){
37799         this.minSize = minSize;
37800     },
37801     
37802     /**
37803      * Gets the maximum size for the resizing element
37804      * @return {Number} The maximum size
37805      */
37806     getMaximumSize : function(){
37807         return this.maxSize;
37808     },
37809     
37810     /**
37811      * Sets the maximum size for the resizing element
37812      * @param {Number} maxSize The maximum size
37813      */
37814     setMaximumSize : function(maxSize){
37815         this.maxSize = maxSize;
37816     },
37817     
37818     /**
37819      * Sets the initialize size for the resizing element
37820      * @param {Number} size The initial size
37821      */
37822     setCurrentSize : function(size){
37823         var oldAnimate = this.animate;
37824         this.animate = false;
37825         this.adapter.setElementSize(this, size);
37826         this.animate = oldAnimate;
37827     },
37828     
37829     /**
37830      * Destroy this splitbar. 
37831      * @param {Boolean} removeEl True to remove the element
37832      */
37833     destroy : function(removeEl){
37834         if(this.shim){
37835             this.shim.remove();
37836         }
37837         this.dd.unreg();
37838         this.proxy.parentNode.removeChild(this.proxy);
37839         if(removeEl){
37840             this.el.remove();
37841         }
37842     }
37843 });
37844
37845 /**
37846  * @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.
37847  */
37848 Roo.bootstrap.SplitBar.createProxy = function(dir){
37849     var proxy = new Roo.Element(document.createElement("div"));
37850     proxy.unselectable();
37851     var cls = 'roo-splitbar-proxy';
37852     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37853     document.body.appendChild(proxy.dom);
37854     return proxy.dom;
37855 };
37856
37857 /** 
37858  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37859  * Default Adapter. It assumes the splitter and resizing element are not positioned
37860  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37861  */
37862 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37863 };
37864
37865 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37866     // do nothing for now
37867     init : function(s){
37868     
37869     },
37870     /**
37871      * Called before drag operations to get the current size of the resizing element. 
37872      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37873      */
37874      getElementSize : function(s){
37875         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37876             return s.resizingEl.getWidth();
37877         }else{
37878             return s.resizingEl.getHeight();
37879         }
37880     },
37881     
37882     /**
37883      * Called after drag operations to set the size of the resizing element.
37884      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37885      * @param {Number} newSize The new size to set
37886      * @param {Function} onComplete A function to be invoked when resizing is complete
37887      */
37888     setElementSize : function(s, newSize, onComplete){
37889         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37890             if(!s.animate){
37891                 s.resizingEl.setWidth(newSize);
37892                 if(onComplete){
37893                     onComplete(s, newSize);
37894                 }
37895             }else{
37896                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37897             }
37898         }else{
37899             
37900             if(!s.animate){
37901                 s.resizingEl.setHeight(newSize);
37902                 if(onComplete){
37903                     onComplete(s, newSize);
37904                 }
37905             }else{
37906                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37907             }
37908         }
37909     }
37910 };
37911
37912 /** 
37913  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37914  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37915  * Adapter that  moves the splitter element to align with the resized sizing element. 
37916  * Used with an absolute positioned SplitBar.
37917  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37918  * document.body, make sure you assign an id to the body element.
37919  */
37920 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37921     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37922     this.container = Roo.get(container);
37923 };
37924
37925 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37926     init : function(s){
37927         this.basic.init(s);
37928     },
37929     
37930     getElementSize : function(s){
37931         return this.basic.getElementSize(s);
37932     },
37933     
37934     setElementSize : function(s, newSize, onComplete){
37935         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37936     },
37937     
37938     moveSplitter : function(s){
37939         var yes = Roo.bootstrap.SplitBar;
37940         switch(s.placement){
37941             case yes.LEFT:
37942                 s.el.setX(s.resizingEl.getRight());
37943                 break;
37944             case yes.RIGHT:
37945                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37946                 break;
37947             case yes.TOP:
37948                 s.el.setY(s.resizingEl.getBottom());
37949                 break;
37950             case yes.BOTTOM:
37951                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37952                 break;
37953         }
37954     }
37955 };
37956
37957 /**
37958  * Orientation constant - Create a vertical SplitBar
37959  * @static
37960  * @type Number
37961  */
37962 Roo.bootstrap.SplitBar.VERTICAL = 1;
37963
37964 /**
37965  * Orientation constant - Create a horizontal SplitBar
37966  * @static
37967  * @type Number
37968  */
37969 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37970
37971 /**
37972  * Placement constant - The resizing element is to the left of the splitter element
37973  * @static
37974  * @type Number
37975  */
37976 Roo.bootstrap.SplitBar.LEFT = 1;
37977
37978 /**
37979  * Placement constant - The resizing element is to the right of the splitter element
37980  * @static
37981  * @type Number
37982  */
37983 Roo.bootstrap.SplitBar.RIGHT = 2;
37984
37985 /**
37986  * Placement constant - The resizing element is positioned above the splitter element
37987  * @static
37988  * @type Number
37989  */
37990 Roo.bootstrap.SplitBar.TOP = 3;
37991
37992 /**
37993  * Placement constant - The resizing element is positioned under splitter element
37994  * @static
37995  * @type Number
37996  */
37997 Roo.bootstrap.SplitBar.BOTTOM = 4;
37998 Roo.namespace("Roo.bootstrap.layout");/*
37999  * Based on:
38000  * Ext JS Library 1.1.1
38001  * Copyright(c) 2006-2007, Ext JS, LLC.
38002  *
38003  * Originally Released Under LGPL - original licence link has changed is not relivant.
38004  *
38005  * Fork - LGPL
38006  * <script type="text/javascript">
38007  */
38008
38009 /**
38010  * @class Roo.bootstrap.layout.Manager
38011  * @extends Roo.bootstrap.Component
38012  * Base class for layout managers.
38013  */
38014 Roo.bootstrap.layout.Manager = function(config)
38015 {
38016     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38017
38018
38019
38020
38021
38022     /** false to disable window resize monitoring @type Boolean */
38023     this.monitorWindowResize = true;
38024     this.regions = {};
38025     this.addEvents({
38026         /**
38027          * @event layout
38028          * Fires when a layout is performed.
38029          * @param {Roo.LayoutManager} this
38030          */
38031         "layout" : true,
38032         /**
38033          * @event regionresized
38034          * Fires when the user resizes a region.
38035          * @param {Roo.LayoutRegion} region The resized region
38036          * @param {Number} newSize The new size (width for east/west, height for north/south)
38037          */
38038         "regionresized" : true,
38039         /**
38040          * @event regioncollapsed
38041          * Fires when a region is collapsed.
38042          * @param {Roo.LayoutRegion} region The collapsed region
38043          */
38044         "regioncollapsed" : true,
38045         /**
38046          * @event regionexpanded
38047          * Fires when a region is expanded.
38048          * @param {Roo.LayoutRegion} region The expanded region
38049          */
38050         "regionexpanded" : true
38051     });
38052     this.updating = false;
38053
38054     if (config.el) {
38055         this.el = Roo.get(config.el);
38056         this.initEvents();
38057     }
38058
38059 };
38060
38061 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38062
38063
38064     regions : null,
38065
38066     monitorWindowResize : true,
38067
38068
38069     updating : false,
38070
38071
38072     onRender : function(ct, position)
38073     {
38074         if(!this.el){
38075             this.el = Roo.get(ct);
38076             this.initEvents();
38077         }
38078         //this.fireEvent('render',this);
38079     },
38080
38081
38082     initEvents: function()
38083     {
38084
38085
38086         // ie scrollbar fix
38087         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38088             document.body.scroll = "no";
38089         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38090             this.el.position('relative');
38091         }
38092         this.id = this.el.id;
38093         this.el.addClass("roo-layout-container");
38094         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38095         if(this.el.dom != document.body ) {
38096             this.el.on('resize', this.layout,this);
38097             this.el.on('show', this.layout,this);
38098         }
38099
38100     },
38101
38102     /**
38103      * Returns true if this layout is currently being updated
38104      * @return {Boolean}
38105      */
38106     isUpdating : function(){
38107         return this.updating;
38108     },
38109
38110     /**
38111      * Suspend the LayoutManager from doing auto-layouts while
38112      * making multiple add or remove calls
38113      */
38114     beginUpdate : function(){
38115         this.updating = true;
38116     },
38117
38118     /**
38119      * Restore auto-layouts and optionally disable the manager from performing a layout
38120      * @param {Boolean} noLayout true to disable a layout update
38121      */
38122     endUpdate : function(noLayout){
38123         this.updating = false;
38124         if(!noLayout){
38125             this.layout();
38126         }
38127     },
38128
38129     layout: function(){
38130         // abstract...
38131     },
38132
38133     onRegionResized : function(region, newSize){
38134         this.fireEvent("regionresized", region, newSize);
38135         this.layout();
38136     },
38137
38138     onRegionCollapsed : function(region){
38139         this.fireEvent("regioncollapsed", region);
38140     },
38141
38142     onRegionExpanded : function(region){
38143         this.fireEvent("regionexpanded", region);
38144     },
38145
38146     /**
38147      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38148      * performs box-model adjustments.
38149      * @return {Object} The size as an object {width: (the width), height: (the height)}
38150      */
38151     getViewSize : function()
38152     {
38153         var size;
38154         if(this.el.dom != document.body){
38155             size = this.el.getSize();
38156         }else{
38157             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38158         }
38159         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38160         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38161         return size;
38162     },
38163
38164     /**
38165      * Returns the Element this layout is bound to.
38166      * @return {Roo.Element}
38167      */
38168     getEl : function(){
38169         return this.el;
38170     },
38171
38172     /**
38173      * Returns the specified region.
38174      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38175      * @return {Roo.LayoutRegion}
38176      */
38177     getRegion : function(target){
38178         return this.regions[target.toLowerCase()];
38179     },
38180
38181     onWindowResize : function(){
38182         if(this.monitorWindowResize){
38183             this.layout();
38184         }
38185     }
38186 });
38187 /*
38188  * Based on:
38189  * Ext JS Library 1.1.1
38190  * Copyright(c) 2006-2007, Ext JS, LLC.
38191  *
38192  * Originally Released Under LGPL - original licence link has changed is not relivant.
38193  *
38194  * Fork - LGPL
38195  * <script type="text/javascript">
38196  */
38197 /**
38198  * @class Roo.bootstrap.layout.Border
38199  * @extends Roo.bootstrap.layout.Manager
38200  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38201  * please see: examples/bootstrap/nested.html<br><br>
38202  
38203 <b>The container the layout is rendered into can be either the body element or any other element.
38204 If it is not the body element, the container needs to either be an absolute positioned element,
38205 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38206 the container size if it is not the body element.</b>
38207
38208 * @constructor
38209 * Create a new Border
38210 * @param {Object} config Configuration options
38211  */
38212 Roo.bootstrap.layout.Border = function(config){
38213     config = config || {};
38214     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38215     
38216     
38217     
38218     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38219         if(config[region]){
38220             config[region].region = region;
38221             this.addRegion(config[region]);
38222         }
38223     },this);
38224     
38225 };
38226
38227 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38228
38229 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38230     
38231     parent : false, // this might point to a 'nest' or a ???
38232     
38233     /**
38234      * Creates and adds a new region if it doesn't already exist.
38235      * @param {String} target The target region key (north, south, east, west or center).
38236      * @param {Object} config The regions config object
38237      * @return {BorderLayoutRegion} The new region
38238      */
38239     addRegion : function(config)
38240     {
38241         if(!this.regions[config.region]){
38242             var r = this.factory(config);
38243             this.bindRegion(r);
38244         }
38245         return this.regions[config.region];
38246     },
38247
38248     // private (kinda)
38249     bindRegion : function(r){
38250         this.regions[r.config.region] = r;
38251         
38252         r.on("visibilitychange",    this.layout, this);
38253         r.on("paneladded",          this.layout, this);
38254         r.on("panelremoved",        this.layout, this);
38255         r.on("invalidated",         this.layout, this);
38256         r.on("resized",             this.onRegionResized, this);
38257         r.on("collapsed",           this.onRegionCollapsed, this);
38258         r.on("expanded",            this.onRegionExpanded, this);
38259     },
38260
38261     /**
38262      * Performs a layout update.
38263      */
38264     layout : function()
38265     {
38266         if(this.updating) {
38267             return;
38268         }
38269         
38270         // render all the rebions if they have not been done alreayd?
38271         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38272             if(this.regions[region] && !this.regions[region].bodyEl){
38273                 this.regions[region].onRender(this.el)
38274             }
38275         },this);
38276         
38277         var size = this.getViewSize();
38278         var w = size.width;
38279         var h = size.height;
38280         var centerW = w;
38281         var centerH = h;
38282         var centerY = 0;
38283         var centerX = 0;
38284         //var x = 0, y = 0;
38285
38286         var rs = this.regions;
38287         var north = rs["north"];
38288         var south = rs["south"]; 
38289         var west = rs["west"];
38290         var east = rs["east"];
38291         var center = rs["center"];
38292         //if(this.hideOnLayout){ // not supported anymore
38293             //c.el.setStyle("display", "none");
38294         //}
38295         if(north && north.isVisible()){
38296             var b = north.getBox();
38297             var m = north.getMargins();
38298             b.width = w - (m.left+m.right);
38299             b.x = m.left;
38300             b.y = m.top;
38301             centerY = b.height + b.y + m.bottom;
38302             centerH -= centerY;
38303             north.updateBox(this.safeBox(b));
38304         }
38305         if(south && south.isVisible()){
38306             var b = south.getBox();
38307             var m = south.getMargins();
38308             b.width = w - (m.left+m.right);
38309             b.x = m.left;
38310             var totalHeight = (b.height + m.top + m.bottom);
38311             b.y = h - totalHeight + m.top;
38312             centerH -= totalHeight;
38313             south.updateBox(this.safeBox(b));
38314         }
38315         if(west && west.isVisible()){
38316             var b = west.getBox();
38317             var m = west.getMargins();
38318             b.height = centerH - (m.top+m.bottom);
38319             b.x = m.left;
38320             b.y = centerY + m.top;
38321             var totalWidth = (b.width + m.left + m.right);
38322             centerX += totalWidth;
38323             centerW -= totalWidth;
38324             west.updateBox(this.safeBox(b));
38325         }
38326         if(east && east.isVisible()){
38327             var b = east.getBox();
38328             var m = east.getMargins();
38329             b.height = centerH - (m.top+m.bottom);
38330             var totalWidth = (b.width + m.left + m.right);
38331             b.x = w - totalWidth + m.left;
38332             b.y = centerY + m.top;
38333             centerW -= totalWidth;
38334             east.updateBox(this.safeBox(b));
38335         }
38336         if(center){
38337             var m = center.getMargins();
38338             var centerBox = {
38339                 x: centerX + m.left,
38340                 y: centerY + m.top,
38341                 width: centerW - (m.left+m.right),
38342                 height: centerH - (m.top+m.bottom)
38343             };
38344             //if(this.hideOnLayout){
38345                 //center.el.setStyle("display", "block");
38346             //}
38347             center.updateBox(this.safeBox(centerBox));
38348         }
38349         this.el.repaint();
38350         this.fireEvent("layout", this);
38351     },
38352
38353     // private
38354     safeBox : function(box){
38355         box.width = Math.max(0, box.width);
38356         box.height = Math.max(0, box.height);
38357         return box;
38358     },
38359
38360     /**
38361      * Adds a ContentPanel (or subclass) to this layout.
38362      * @param {String} target The target region key (north, south, east, west or center).
38363      * @param {Roo.ContentPanel} panel The panel to add
38364      * @return {Roo.ContentPanel} The added panel
38365      */
38366     add : function(target, panel){
38367          
38368         target = target.toLowerCase();
38369         return this.regions[target].add(panel);
38370     },
38371
38372     /**
38373      * Remove a ContentPanel (or subclass) to this layout.
38374      * @param {String} target The target region key (north, south, east, west or center).
38375      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38376      * @return {Roo.ContentPanel} The removed panel
38377      */
38378     remove : function(target, panel){
38379         target = target.toLowerCase();
38380         return this.regions[target].remove(panel);
38381     },
38382
38383     /**
38384      * Searches all regions for a panel with the specified id
38385      * @param {String} panelId
38386      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38387      */
38388     findPanel : function(panelId){
38389         var rs = this.regions;
38390         for(var target in rs){
38391             if(typeof rs[target] != "function"){
38392                 var p = rs[target].getPanel(panelId);
38393                 if(p){
38394                     return p;
38395                 }
38396             }
38397         }
38398         return null;
38399     },
38400
38401     /**
38402      * Searches all regions for a panel with the specified id and activates (shows) it.
38403      * @param {String/ContentPanel} panelId The panels id or the panel itself
38404      * @return {Roo.ContentPanel} The shown panel or null
38405      */
38406     showPanel : function(panelId) {
38407       var rs = this.regions;
38408       for(var target in rs){
38409          var r = rs[target];
38410          if(typeof r != "function"){
38411             if(r.hasPanel(panelId)){
38412                return r.showPanel(panelId);
38413             }
38414          }
38415       }
38416       return null;
38417    },
38418
38419    /**
38420      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38421      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38422      */
38423    /*
38424     restoreState : function(provider){
38425         if(!provider){
38426             provider = Roo.state.Manager;
38427         }
38428         var sm = new Roo.LayoutStateManager();
38429         sm.init(this, provider);
38430     },
38431 */
38432  
38433  
38434     /**
38435      * Adds a xtype elements to the layout.
38436      * <pre><code>
38437
38438 layout.addxtype({
38439        xtype : 'ContentPanel',
38440        region: 'west',
38441        items: [ .... ]
38442    }
38443 );
38444
38445 layout.addxtype({
38446         xtype : 'NestedLayoutPanel',
38447         region: 'west',
38448         layout: {
38449            center: { },
38450            west: { }   
38451         },
38452         items : [ ... list of content panels or nested layout panels.. ]
38453    }
38454 );
38455 </code></pre>
38456      * @param {Object} cfg Xtype definition of item to add.
38457      */
38458     addxtype : function(cfg)
38459     {
38460         // basically accepts a pannel...
38461         // can accept a layout region..!?!?
38462         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38463         
38464         
38465         // theory?  children can only be panels??
38466         
38467         //if (!cfg.xtype.match(/Panel$/)) {
38468         //    return false;
38469         //}
38470         var ret = false;
38471         
38472         if (typeof(cfg.region) == 'undefined') {
38473             Roo.log("Failed to add Panel, region was not set");
38474             Roo.log(cfg);
38475             return false;
38476         }
38477         var region = cfg.region;
38478         delete cfg.region;
38479         
38480           
38481         var xitems = [];
38482         if (cfg.items) {
38483             xitems = cfg.items;
38484             delete cfg.items;
38485         }
38486         var nb = false;
38487         
38488         if ( region == 'center') {
38489             Roo.log("Center: " + cfg.title);
38490         }
38491         
38492         
38493         switch(cfg.xtype) 
38494         {
38495             case 'Content':  // ContentPanel (el, cfg)
38496             case 'Scroll':  // ContentPanel (el, cfg)
38497             case 'View': 
38498                 cfg.autoCreate = cfg.autoCreate || true;
38499                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38500                 //} else {
38501                 //    var el = this.el.createChild();
38502                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38503                 //}
38504                 
38505                 this.add(region, ret);
38506                 break;
38507             
38508             /*
38509             case 'TreePanel': // our new panel!
38510                 cfg.el = this.el.createChild();
38511                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38512                 this.add(region, ret);
38513                 break;
38514             */
38515             
38516             case 'Nest': 
38517                 // create a new Layout (which is  a Border Layout...
38518                 
38519                 var clayout = cfg.layout;
38520                 clayout.el  = this.el.createChild();
38521                 clayout.items   = clayout.items  || [];
38522                 
38523                 delete cfg.layout;
38524                 
38525                 // replace this exitems with the clayout ones..
38526                 xitems = clayout.items;
38527                  
38528                 // force background off if it's in center...
38529                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38530                     cfg.background = false;
38531                 }
38532                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38533                 
38534                 
38535                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38536                 //console.log('adding nested layout panel '  + cfg.toSource());
38537                 this.add(region, ret);
38538                 nb = {}; /// find first...
38539                 break;
38540             
38541             case 'Grid':
38542                 
38543                 // needs grid and region
38544                 
38545                 //var el = this.getRegion(region).el.createChild();
38546                 /*
38547                  *var el = this.el.createChild();
38548                 // create the grid first...
38549                 cfg.grid.container = el;
38550                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38551                 */
38552                 
38553                 if (region == 'center' && this.active ) {
38554                     cfg.background = false;
38555                 }
38556                 
38557                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38558                 
38559                 this.add(region, ret);
38560                 /*
38561                 if (cfg.background) {
38562                     // render grid on panel activation (if panel background)
38563                     ret.on('activate', function(gp) {
38564                         if (!gp.grid.rendered) {
38565                     //        gp.grid.render(el);
38566                         }
38567                     });
38568                 } else {
38569                   //  cfg.grid.render(el);
38570                 }
38571                 */
38572                 break;
38573            
38574            
38575             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38576                 // it was the old xcomponent building that caused this before.
38577                 // espeically if border is the top element in the tree.
38578                 ret = this;
38579                 break; 
38580                 
38581                     
38582                 
38583                 
38584                 
38585             default:
38586                 /*
38587                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38588                     
38589                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38590                     this.add(region, ret);
38591                 } else {
38592                 */
38593                     Roo.log(cfg);
38594                     throw "Can not add '" + cfg.xtype + "' to Border";
38595                     return null;
38596              
38597                                 
38598              
38599         }
38600         this.beginUpdate();
38601         // add children..
38602         var region = '';
38603         var abn = {};
38604         Roo.each(xitems, function(i)  {
38605             region = nb && i.region ? i.region : false;
38606             
38607             var add = ret.addxtype(i);
38608            
38609             if (region) {
38610                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38611                 if (!i.background) {
38612                     abn[region] = nb[region] ;
38613                 }
38614             }
38615             
38616         });
38617         this.endUpdate();
38618
38619         // make the last non-background panel active..
38620         //if (nb) { Roo.log(abn); }
38621         if (nb) {
38622             
38623             for(var r in abn) {
38624                 region = this.getRegion(r);
38625                 if (region) {
38626                     // tried using nb[r], but it does not work..
38627                      
38628                     region.showPanel(abn[r]);
38629                    
38630                 }
38631             }
38632         }
38633         return ret;
38634         
38635     },
38636     
38637     
38638 // private
38639     factory : function(cfg)
38640     {
38641         
38642         var validRegions = Roo.bootstrap.layout.Border.regions;
38643
38644         var target = cfg.region;
38645         cfg.mgr = this;
38646         
38647         var r = Roo.bootstrap.layout;
38648         Roo.log(target);
38649         switch(target){
38650             case "north":
38651                 return new r.North(cfg);
38652             case "south":
38653                 return new r.South(cfg);
38654             case "east":
38655                 return new r.East(cfg);
38656             case "west":
38657                 return new r.West(cfg);
38658             case "center":
38659                 return new r.Center(cfg);
38660         }
38661         throw 'Layout region "'+target+'" not supported.';
38662     }
38663     
38664     
38665 });
38666  /*
38667  * Based on:
38668  * Ext JS Library 1.1.1
38669  * Copyright(c) 2006-2007, Ext JS, LLC.
38670  *
38671  * Originally Released Under LGPL - original licence link has changed is not relivant.
38672  *
38673  * Fork - LGPL
38674  * <script type="text/javascript">
38675  */
38676  
38677 /**
38678  * @class Roo.bootstrap.layout.Basic
38679  * @extends Roo.util.Observable
38680  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38681  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38682  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38683  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38684  * @cfg {string}   region  the region that it inhabits..
38685  * @cfg {bool}   skipConfig skip config?
38686  * 
38687
38688  */
38689 Roo.bootstrap.layout.Basic = function(config){
38690     
38691     this.mgr = config.mgr;
38692     
38693     this.position = config.region;
38694     
38695     var skipConfig = config.skipConfig;
38696     
38697     this.events = {
38698         /**
38699          * @scope Roo.BasicLayoutRegion
38700          */
38701         
38702         /**
38703          * @event beforeremove
38704          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38705          * @param {Roo.LayoutRegion} this
38706          * @param {Roo.ContentPanel} panel The panel
38707          * @param {Object} e The cancel event object
38708          */
38709         "beforeremove" : true,
38710         /**
38711          * @event invalidated
38712          * Fires when the layout for this region is changed.
38713          * @param {Roo.LayoutRegion} this
38714          */
38715         "invalidated" : true,
38716         /**
38717          * @event visibilitychange
38718          * Fires when this region is shown or hidden 
38719          * @param {Roo.LayoutRegion} this
38720          * @param {Boolean} visibility true or false
38721          */
38722         "visibilitychange" : true,
38723         /**
38724          * @event paneladded
38725          * Fires when a panel is added. 
38726          * @param {Roo.LayoutRegion} this
38727          * @param {Roo.ContentPanel} panel The panel
38728          */
38729         "paneladded" : true,
38730         /**
38731          * @event panelremoved
38732          * Fires when a panel is removed. 
38733          * @param {Roo.LayoutRegion} this
38734          * @param {Roo.ContentPanel} panel The panel
38735          */
38736         "panelremoved" : true,
38737         /**
38738          * @event beforecollapse
38739          * Fires when this region before collapse.
38740          * @param {Roo.LayoutRegion} this
38741          */
38742         "beforecollapse" : true,
38743         /**
38744          * @event collapsed
38745          * Fires when this region is collapsed.
38746          * @param {Roo.LayoutRegion} this
38747          */
38748         "collapsed" : true,
38749         /**
38750          * @event expanded
38751          * Fires when this region is expanded.
38752          * @param {Roo.LayoutRegion} this
38753          */
38754         "expanded" : true,
38755         /**
38756          * @event slideshow
38757          * Fires when this region is slid into view.
38758          * @param {Roo.LayoutRegion} this
38759          */
38760         "slideshow" : true,
38761         /**
38762          * @event slidehide
38763          * Fires when this region slides out of view. 
38764          * @param {Roo.LayoutRegion} this
38765          */
38766         "slidehide" : true,
38767         /**
38768          * @event panelactivated
38769          * Fires when a panel is activated. 
38770          * @param {Roo.LayoutRegion} this
38771          * @param {Roo.ContentPanel} panel The activated panel
38772          */
38773         "panelactivated" : true,
38774         /**
38775          * @event resized
38776          * Fires when the user resizes this region. 
38777          * @param {Roo.LayoutRegion} this
38778          * @param {Number} newSize The new size (width for east/west, height for north/south)
38779          */
38780         "resized" : true
38781     };
38782     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38783     this.panels = new Roo.util.MixedCollection();
38784     this.panels.getKey = this.getPanelId.createDelegate(this);
38785     this.box = null;
38786     this.activePanel = null;
38787     // ensure listeners are added...
38788     
38789     if (config.listeners || config.events) {
38790         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38791             listeners : config.listeners || {},
38792             events : config.events || {}
38793         });
38794     }
38795     
38796     if(skipConfig !== true){
38797         this.applyConfig(config);
38798     }
38799 };
38800
38801 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38802 {
38803     getPanelId : function(p){
38804         return p.getId();
38805     },
38806     
38807     applyConfig : function(config){
38808         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38809         this.config = config;
38810         
38811     },
38812     
38813     /**
38814      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38815      * the width, for horizontal (north, south) the height.
38816      * @param {Number} newSize The new width or height
38817      */
38818     resizeTo : function(newSize){
38819         var el = this.el ? this.el :
38820                  (this.activePanel ? this.activePanel.getEl() : null);
38821         if(el){
38822             switch(this.position){
38823                 case "east":
38824                 case "west":
38825                     el.setWidth(newSize);
38826                     this.fireEvent("resized", this, newSize);
38827                 break;
38828                 case "north":
38829                 case "south":
38830                     el.setHeight(newSize);
38831                     this.fireEvent("resized", this, newSize);
38832                 break;                
38833             }
38834         }
38835     },
38836     
38837     getBox : function(){
38838         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38839     },
38840     
38841     getMargins : function(){
38842         return this.margins;
38843     },
38844     
38845     updateBox : function(box){
38846         this.box = box;
38847         var el = this.activePanel.getEl();
38848         el.dom.style.left = box.x + "px";
38849         el.dom.style.top = box.y + "px";
38850         this.activePanel.setSize(box.width, box.height);
38851     },
38852     
38853     /**
38854      * Returns the container element for this region.
38855      * @return {Roo.Element}
38856      */
38857     getEl : function(){
38858         return this.activePanel;
38859     },
38860     
38861     /**
38862      * Returns true if this region is currently visible.
38863      * @return {Boolean}
38864      */
38865     isVisible : function(){
38866         return this.activePanel ? true : false;
38867     },
38868     
38869     setActivePanel : function(panel){
38870         panel = this.getPanel(panel);
38871         if(this.activePanel && this.activePanel != panel){
38872             this.activePanel.setActiveState(false);
38873             this.activePanel.getEl().setLeftTop(-10000,-10000);
38874         }
38875         this.activePanel = panel;
38876         panel.setActiveState(true);
38877         if(this.box){
38878             panel.setSize(this.box.width, this.box.height);
38879         }
38880         this.fireEvent("panelactivated", this, panel);
38881         this.fireEvent("invalidated");
38882     },
38883     
38884     /**
38885      * Show the specified panel.
38886      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38887      * @return {Roo.ContentPanel} The shown panel or null
38888      */
38889     showPanel : function(panel){
38890         panel = this.getPanel(panel);
38891         if(panel){
38892             this.setActivePanel(panel);
38893         }
38894         return panel;
38895     },
38896     
38897     /**
38898      * Get the active panel for this region.
38899      * @return {Roo.ContentPanel} The active panel or null
38900      */
38901     getActivePanel : function(){
38902         return this.activePanel;
38903     },
38904     
38905     /**
38906      * Add the passed ContentPanel(s)
38907      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38908      * @return {Roo.ContentPanel} The panel added (if only one was added)
38909      */
38910     add : function(panel){
38911         if(arguments.length > 1){
38912             for(var i = 0, len = arguments.length; i < len; i++) {
38913                 this.add(arguments[i]);
38914             }
38915             return null;
38916         }
38917         if(this.hasPanel(panel)){
38918             this.showPanel(panel);
38919             return panel;
38920         }
38921         var el = panel.getEl();
38922         if(el.dom.parentNode != this.mgr.el.dom){
38923             this.mgr.el.dom.appendChild(el.dom);
38924         }
38925         if(panel.setRegion){
38926             panel.setRegion(this);
38927         }
38928         this.panels.add(panel);
38929         el.setStyle("position", "absolute");
38930         if(!panel.background){
38931             this.setActivePanel(panel);
38932             if(this.config.initialSize && this.panels.getCount()==1){
38933                 this.resizeTo(this.config.initialSize);
38934             }
38935         }
38936         this.fireEvent("paneladded", this, panel);
38937         return panel;
38938     },
38939     
38940     /**
38941      * Returns true if the panel is in this region.
38942      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38943      * @return {Boolean}
38944      */
38945     hasPanel : function(panel){
38946         if(typeof panel == "object"){ // must be panel obj
38947             panel = panel.getId();
38948         }
38949         return this.getPanel(panel) ? true : false;
38950     },
38951     
38952     /**
38953      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38954      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38955      * @param {Boolean} preservePanel Overrides the config preservePanel option
38956      * @return {Roo.ContentPanel} The panel that was removed
38957      */
38958     remove : function(panel, preservePanel){
38959         panel = this.getPanel(panel);
38960         if(!panel){
38961             return null;
38962         }
38963         var e = {};
38964         this.fireEvent("beforeremove", this, panel, e);
38965         if(e.cancel === true){
38966             return null;
38967         }
38968         var panelId = panel.getId();
38969         this.panels.removeKey(panelId);
38970         return panel;
38971     },
38972     
38973     /**
38974      * Returns the panel specified or null if it's not in this region.
38975      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38976      * @return {Roo.ContentPanel}
38977      */
38978     getPanel : function(id){
38979         if(typeof id == "object"){ // must be panel obj
38980             return id;
38981         }
38982         return this.panels.get(id);
38983     },
38984     
38985     /**
38986      * Returns this regions position (north/south/east/west/center).
38987      * @return {String} 
38988      */
38989     getPosition: function(){
38990         return this.position;    
38991     }
38992 });/*
38993  * Based on:
38994  * Ext JS Library 1.1.1
38995  * Copyright(c) 2006-2007, Ext JS, LLC.
38996  *
38997  * Originally Released Under LGPL - original licence link has changed is not relivant.
38998  *
38999  * Fork - LGPL
39000  * <script type="text/javascript">
39001  */
39002  
39003 /**
39004  * @class Roo.bootstrap.layout.Region
39005  * @extends Roo.bootstrap.layout.Basic
39006  * This class represents a region in a layout manager.
39007  
39008  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39009  * @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})
39010  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39011  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39012  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39013  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39014  * @cfg {String}    title           The title for the region (overrides panel titles)
39015  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39016  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39017  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39018  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39019  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39020  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39021  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39022  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39023  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39024  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39025
39026  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39027  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39028  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39029  * @cfg {Number}    width           For East/West panels
39030  * @cfg {Number}    height          For North/South panels
39031  * @cfg {Boolean}   split           To show the splitter
39032  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39033  * 
39034  * @cfg {string}   cls             Extra CSS classes to add to region
39035  * 
39036  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39037  * @cfg {string}   region  the region that it inhabits..
39038  *
39039
39040  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39041  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39042
39043  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39044  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39045  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39046  */
39047 Roo.bootstrap.layout.Region = function(config)
39048 {
39049     this.applyConfig(config);
39050
39051     var mgr = config.mgr;
39052     var pos = config.region;
39053     config.skipConfig = true;
39054     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39055     
39056     if (mgr.el) {
39057         this.onRender(mgr.el);   
39058     }
39059      
39060     this.visible = true;
39061     this.collapsed = false;
39062     this.unrendered_panels = [];
39063 };
39064
39065 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39066
39067     position: '', // set by wrapper (eg. north/south etc..)
39068     unrendered_panels : null,  // unrendered panels.
39069     
39070     tabPosition : false,
39071     
39072     mgr: false, // points to 'Border'
39073     
39074     
39075     createBody : function(){
39076         /** This region's body element 
39077         * @type Roo.Element */
39078         this.bodyEl = this.el.createChild({
39079                 tag: "div",
39080                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39081         });
39082     },
39083
39084     onRender: function(ctr, pos)
39085     {
39086         var dh = Roo.DomHelper;
39087         /** This region's container element 
39088         * @type Roo.Element */
39089         this.el = dh.append(ctr.dom, {
39090                 tag: "div",
39091                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39092             }, true);
39093         /** This region's title element 
39094         * @type Roo.Element */
39095     
39096         this.titleEl = dh.append(this.el.dom,  {
39097                 tag: "div",
39098                 unselectable: "on",
39099                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39100                 children:[
39101                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39102                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39103                 ]
39104             }, true);
39105         
39106         this.titleEl.enableDisplayMode();
39107         /** This region's title text element 
39108         * @type HTMLElement */
39109         this.titleTextEl = this.titleEl.dom.firstChild;
39110         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39111         /*
39112         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39113         this.closeBtn.enableDisplayMode();
39114         this.closeBtn.on("click", this.closeClicked, this);
39115         this.closeBtn.hide();
39116     */
39117         this.createBody(this.config);
39118         if(this.config.hideWhenEmpty){
39119             this.hide();
39120             this.on("paneladded", this.validateVisibility, this);
39121             this.on("panelremoved", this.validateVisibility, this);
39122         }
39123         if(this.autoScroll){
39124             this.bodyEl.setStyle("overflow", "auto");
39125         }else{
39126             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39127         }
39128         //if(c.titlebar !== false){
39129             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39130                 this.titleEl.hide();
39131             }else{
39132                 this.titleEl.show();
39133                 if(this.config.title){
39134                     this.titleTextEl.innerHTML = this.config.title;
39135                 }
39136             }
39137         //}
39138         if(this.config.collapsed){
39139             this.collapse(true);
39140         }
39141         if(this.config.hidden){
39142             this.hide();
39143         }
39144         
39145         if (this.unrendered_panels && this.unrendered_panels.length) {
39146             for (var i =0;i< this.unrendered_panels.length; i++) {
39147                 this.add(this.unrendered_panels[i]);
39148             }
39149             this.unrendered_panels = null;
39150             
39151         }
39152         
39153     },
39154     
39155     applyConfig : function(c)
39156     {
39157         /*
39158          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39159             var dh = Roo.DomHelper;
39160             if(c.titlebar !== false){
39161                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39162                 this.collapseBtn.on("click", this.collapse, this);
39163                 this.collapseBtn.enableDisplayMode();
39164                 /*
39165                 if(c.showPin === true || this.showPin){
39166                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39167                     this.stickBtn.enableDisplayMode();
39168                     this.stickBtn.on("click", this.expand, this);
39169                     this.stickBtn.hide();
39170                 }
39171                 
39172             }
39173             */
39174             /** This region's collapsed element
39175             * @type Roo.Element */
39176             /*
39177              *
39178             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39179                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39180             ]}, true);
39181             
39182             if(c.floatable !== false){
39183                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39184                this.collapsedEl.on("click", this.collapseClick, this);
39185             }
39186
39187             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39188                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39189                    id: "message", unselectable: "on", style:{"float":"left"}});
39190                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39191              }
39192             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39193             this.expandBtn.on("click", this.expand, this);
39194             
39195         }
39196         
39197         if(this.collapseBtn){
39198             this.collapseBtn.setVisible(c.collapsible == true);
39199         }
39200         
39201         this.cmargins = c.cmargins || this.cmargins ||
39202                          (this.position == "west" || this.position == "east" ?
39203                              {top: 0, left: 2, right:2, bottom: 0} :
39204                              {top: 2, left: 0, right:0, bottom: 2});
39205         */
39206         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39207         
39208         
39209         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39210         
39211         this.autoScroll = c.autoScroll || false;
39212         
39213         
39214        
39215         
39216         this.duration = c.duration || .30;
39217         this.slideDuration = c.slideDuration || .45;
39218         this.config = c;
39219        
39220     },
39221     /**
39222      * Returns true if this region is currently visible.
39223      * @return {Boolean}
39224      */
39225     isVisible : function(){
39226         return this.visible;
39227     },
39228
39229     /**
39230      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39231      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39232      */
39233     //setCollapsedTitle : function(title){
39234     //    title = title || "&#160;";
39235      //   if(this.collapsedTitleTextEl){
39236       //      this.collapsedTitleTextEl.innerHTML = title;
39237        // }
39238     //},
39239
39240     getBox : function(){
39241         var b;
39242       //  if(!this.collapsed){
39243             b = this.el.getBox(false, true);
39244        // }else{
39245           //  b = this.collapsedEl.getBox(false, true);
39246         //}
39247         return b;
39248     },
39249
39250     getMargins : function(){
39251         return this.margins;
39252         //return this.collapsed ? this.cmargins : this.margins;
39253     },
39254 /*
39255     highlight : function(){
39256         this.el.addClass("x-layout-panel-dragover");
39257     },
39258
39259     unhighlight : function(){
39260         this.el.removeClass("x-layout-panel-dragover");
39261     },
39262 */
39263     updateBox : function(box)
39264     {
39265         if (!this.bodyEl) {
39266             return; // not rendered yet..
39267         }
39268         
39269         this.box = box;
39270         if(!this.collapsed){
39271             this.el.dom.style.left = box.x + "px";
39272             this.el.dom.style.top = box.y + "px";
39273             this.updateBody(box.width, box.height);
39274         }else{
39275             this.collapsedEl.dom.style.left = box.x + "px";
39276             this.collapsedEl.dom.style.top = box.y + "px";
39277             this.collapsedEl.setSize(box.width, box.height);
39278         }
39279         if(this.tabs){
39280             this.tabs.autoSizeTabs();
39281         }
39282     },
39283
39284     updateBody : function(w, h)
39285     {
39286         if(w !== null){
39287             this.el.setWidth(w);
39288             w -= this.el.getBorderWidth("rl");
39289             if(this.config.adjustments){
39290                 w += this.config.adjustments[0];
39291             }
39292         }
39293         if(h !== null && h > 0){
39294             this.el.setHeight(h);
39295             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39296             h -= this.el.getBorderWidth("tb");
39297             if(this.config.adjustments){
39298                 h += this.config.adjustments[1];
39299             }
39300             this.bodyEl.setHeight(h);
39301             if(this.tabs){
39302                 h = this.tabs.syncHeight(h);
39303             }
39304         }
39305         if(this.panelSize){
39306             w = w !== null ? w : this.panelSize.width;
39307             h = h !== null ? h : this.panelSize.height;
39308         }
39309         if(this.activePanel){
39310             var el = this.activePanel.getEl();
39311             w = w !== null ? w : el.getWidth();
39312             h = h !== null ? h : el.getHeight();
39313             this.panelSize = {width: w, height: h};
39314             this.activePanel.setSize(w, h);
39315         }
39316         if(Roo.isIE && this.tabs){
39317             this.tabs.el.repaint();
39318         }
39319     },
39320
39321     /**
39322      * Returns the container element for this region.
39323      * @return {Roo.Element}
39324      */
39325     getEl : function(){
39326         return this.el;
39327     },
39328
39329     /**
39330      * Hides this region.
39331      */
39332     hide : function(){
39333         //if(!this.collapsed){
39334             this.el.dom.style.left = "-2000px";
39335             this.el.hide();
39336         //}else{
39337          //   this.collapsedEl.dom.style.left = "-2000px";
39338          //   this.collapsedEl.hide();
39339        // }
39340         this.visible = false;
39341         this.fireEvent("visibilitychange", this, false);
39342     },
39343
39344     /**
39345      * Shows this region if it was previously hidden.
39346      */
39347     show : function(){
39348         //if(!this.collapsed){
39349             this.el.show();
39350         //}else{
39351         //    this.collapsedEl.show();
39352        // }
39353         this.visible = true;
39354         this.fireEvent("visibilitychange", this, true);
39355     },
39356 /*
39357     closeClicked : function(){
39358         if(this.activePanel){
39359             this.remove(this.activePanel);
39360         }
39361     },
39362
39363     collapseClick : function(e){
39364         if(this.isSlid){
39365            e.stopPropagation();
39366            this.slideIn();
39367         }else{
39368            e.stopPropagation();
39369            this.slideOut();
39370         }
39371     },
39372 */
39373     /**
39374      * Collapses this region.
39375      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39376      */
39377     /*
39378     collapse : function(skipAnim, skipCheck = false){
39379         if(this.collapsed) {
39380             return;
39381         }
39382         
39383         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39384             
39385             this.collapsed = true;
39386             if(this.split){
39387                 this.split.el.hide();
39388             }
39389             if(this.config.animate && skipAnim !== true){
39390                 this.fireEvent("invalidated", this);
39391                 this.animateCollapse();
39392             }else{
39393                 this.el.setLocation(-20000,-20000);
39394                 this.el.hide();
39395                 this.collapsedEl.show();
39396                 this.fireEvent("collapsed", this);
39397                 this.fireEvent("invalidated", this);
39398             }
39399         }
39400         
39401     },
39402 */
39403     animateCollapse : function(){
39404         // overridden
39405     },
39406
39407     /**
39408      * Expands this region if it was previously collapsed.
39409      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39410      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39411      */
39412     /*
39413     expand : function(e, skipAnim){
39414         if(e) {
39415             e.stopPropagation();
39416         }
39417         if(!this.collapsed || this.el.hasActiveFx()) {
39418             return;
39419         }
39420         if(this.isSlid){
39421             this.afterSlideIn();
39422             skipAnim = true;
39423         }
39424         this.collapsed = false;
39425         if(this.config.animate && skipAnim !== true){
39426             this.animateExpand();
39427         }else{
39428             this.el.show();
39429             if(this.split){
39430                 this.split.el.show();
39431             }
39432             this.collapsedEl.setLocation(-2000,-2000);
39433             this.collapsedEl.hide();
39434             this.fireEvent("invalidated", this);
39435             this.fireEvent("expanded", this);
39436         }
39437     },
39438 */
39439     animateExpand : function(){
39440         // overridden
39441     },
39442
39443     initTabs : function()
39444     {
39445         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39446         
39447         var ts = new Roo.bootstrap.panel.Tabs({
39448             el: this.bodyEl.dom,
39449             region : this,
39450             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39451             disableTooltips: this.config.disableTabTips,
39452             toolbar : this.config.toolbar
39453         });
39454         
39455         if(this.config.hideTabs){
39456             ts.stripWrap.setDisplayed(false);
39457         }
39458         this.tabs = ts;
39459         ts.resizeTabs = this.config.resizeTabs === true;
39460         ts.minTabWidth = this.config.minTabWidth || 40;
39461         ts.maxTabWidth = this.config.maxTabWidth || 250;
39462         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39463         ts.monitorResize = false;
39464         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39465         ts.bodyEl.addClass('roo-layout-tabs-body');
39466         this.panels.each(this.initPanelAsTab, this);
39467     },
39468
39469     initPanelAsTab : function(panel){
39470         var ti = this.tabs.addTab(
39471             panel.getEl().id,
39472             panel.getTitle(),
39473             null,
39474             this.config.closeOnTab && panel.isClosable(),
39475             panel.tpl
39476         );
39477         if(panel.tabTip !== undefined){
39478             ti.setTooltip(panel.tabTip);
39479         }
39480         ti.on("activate", function(){
39481               this.setActivePanel(panel);
39482         }, this);
39483         
39484         if(this.config.closeOnTab){
39485             ti.on("beforeclose", function(t, e){
39486                 e.cancel = true;
39487                 this.remove(panel);
39488             }, this);
39489         }
39490         
39491         panel.tabItem = ti;
39492         
39493         return ti;
39494     },
39495
39496     updatePanelTitle : function(panel, title)
39497     {
39498         if(this.activePanel == panel){
39499             this.updateTitle(title);
39500         }
39501         if(this.tabs){
39502             var ti = this.tabs.getTab(panel.getEl().id);
39503             ti.setText(title);
39504             if(panel.tabTip !== undefined){
39505                 ti.setTooltip(panel.tabTip);
39506             }
39507         }
39508     },
39509
39510     updateTitle : function(title){
39511         if(this.titleTextEl && !this.config.title){
39512             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39513         }
39514     },
39515
39516     setActivePanel : function(panel)
39517     {
39518         panel = this.getPanel(panel);
39519         if(this.activePanel && this.activePanel != panel){
39520             if(this.activePanel.setActiveState(false) === false){
39521                 return;
39522             }
39523         }
39524         this.activePanel = panel;
39525         panel.setActiveState(true);
39526         if(this.panelSize){
39527             panel.setSize(this.panelSize.width, this.panelSize.height);
39528         }
39529         if(this.closeBtn){
39530             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39531         }
39532         this.updateTitle(panel.getTitle());
39533         if(this.tabs){
39534             this.fireEvent("invalidated", this);
39535         }
39536         this.fireEvent("panelactivated", this, panel);
39537     },
39538
39539     /**
39540      * Shows the specified panel.
39541      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39542      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39543      */
39544     showPanel : function(panel)
39545     {
39546         panel = this.getPanel(panel);
39547         if(panel){
39548             if(this.tabs){
39549                 var tab = this.tabs.getTab(panel.getEl().id);
39550                 if(tab.isHidden()){
39551                     this.tabs.unhideTab(tab.id);
39552                 }
39553                 tab.activate();
39554             }else{
39555                 this.setActivePanel(panel);
39556             }
39557         }
39558         return panel;
39559     },
39560
39561     /**
39562      * Get the active panel for this region.
39563      * @return {Roo.ContentPanel} The active panel or null
39564      */
39565     getActivePanel : function(){
39566         return this.activePanel;
39567     },
39568
39569     validateVisibility : function(){
39570         if(this.panels.getCount() < 1){
39571             this.updateTitle("&#160;");
39572             this.closeBtn.hide();
39573             this.hide();
39574         }else{
39575             if(!this.isVisible()){
39576                 this.show();
39577             }
39578         }
39579     },
39580
39581     /**
39582      * Adds the passed ContentPanel(s) to this region.
39583      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39584      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39585      */
39586     add : function(panel)
39587     {
39588         if(arguments.length > 1){
39589             for(var i = 0, len = arguments.length; i < len; i++) {
39590                 this.add(arguments[i]);
39591             }
39592             return null;
39593         }
39594         
39595         // if we have not been rendered yet, then we can not really do much of this..
39596         if (!this.bodyEl) {
39597             this.unrendered_panels.push(panel);
39598             return panel;
39599         }
39600         
39601         
39602         
39603         
39604         if(this.hasPanel(panel)){
39605             this.showPanel(panel);
39606             return panel;
39607         }
39608         panel.setRegion(this);
39609         this.panels.add(panel);
39610        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39611             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39612             // and hide them... ???
39613             this.bodyEl.dom.appendChild(panel.getEl().dom);
39614             if(panel.background !== true){
39615                 this.setActivePanel(panel);
39616             }
39617             this.fireEvent("paneladded", this, panel);
39618             return panel;
39619         }
39620         */
39621         if(!this.tabs){
39622             this.initTabs();
39623         }else{
39624             this.initPanelAsTab(panel);
39625         }
39626         
39627         
39628         if(panel.background !== true){
39629             this.tabs.activate(panel.getEl().id);
39630         }
39631         this.fireEvent("paneladded", this, panel);
39632         return panel;
39633     },
39634
39635     /**
39636      * Hides the tab for the specified panel.
39637      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39638      */
39639     hidePanel : function(panel){
39640         if(this.tabs && (panel = this.getPanel(panel))){
39641             this.tabs.hideTab(panel.getEl().id);
39642         }
39643     },
39644
39645     /**
39646      * Unhides the tab for a previously hidden panel.
39647      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39648      */
39649     unhidePanel : function(panel){
39650         if(this.tabs && (panel = this.getPanel(panel))){
39651             this.tabs.unhideTab(panel.getEl().id);
39652         }
39653     },
39654
39655     clearPanels : function(){
39656         while(this.panels.getCount() > 0){
39657              this.remove(this.panels.first());
39658         }
39659     },
39660
39661     /**
39662      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39663      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39664      * @param {Boolean} preservePanel Overrides the config preservePanel option
39665      * @return {Roo.ContentPanel} The panel that was removed
39666      */
39667     remove : function(panel, preservePanel)
39668     {
39669         panel = this.getPanel(panel);
39670         if(!panel){
39671             return null;
39672         }
39673         var e = {};
39674         this.fireEvent("beforeremove", this, panel, e);
39675         if(e.cancel === true){
39676             return null;
39677         }
39678         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39679         var panelId = panel.getId();
39680         this.panels.removeKey(panelId);
39681         if(preservePanel){
39682             document.body.appendChild(panel.getEl().dom);
39683         }
39684         if(this.tabs){
39685             this.tabs.removeTab(panel.getEl().id);
39686         }else if (!preservePanel){
39687             this.bodyEl.dom.removeChild(panel.getEl().dom);
39688         }
39689         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39690             var p = this.panels.first();
39691             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39692             tempEl.appendChild(p.getEl().dom);
39693             this.bodyEl.update("");
39694             this.bodyEl.dom.appendChild(p.getEl().dom);
39695             tempEl = null;
39696             this.updateTitle(p.getTitle());
39697             this.tabs = null;
39698             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39699             this.setActivePanel(p);
39700         }
39701         panel.setRegion(null);
39702         if(this.activePanel == panel){
39703             this.activePanel = null;
39704         }
39705         if(this.config.autoDestroy !== false && preservePanel !== true){
39706             try{panel.destroy();}catch(e){}
39707         }
39708         this.fireEvent("panelremoved", this, panel);
39709         return panel;
39710     },
39711
39712     /**
39713      * Returns the TabPanel component used by this region
39714      * @return {Roo.TabPanel}
39715      */
39716     getTabs : function(){
39717         return this.tabs;
39718     },
39719
39720     createTool : function(parentEl, className){
39721         var btn = Roo.DomHelper.append(parentEl, {
39722             tag: "div",
39723             cls: "x-layout-tools-button",
39724             children: [ {
39725                 tag: "div",
39726                 cls: "roo-layout-tools-button-inner " + className,
39727                 html: "&#160;"
39728             }]
39729         }, true);
39730         btn.addClassOnOver("roo-layout-tools-button-over");
39731         return btn;
39732     }
39733 });/*
39734  * Based on:
39735  * Ext JS Library 1.1.1
39736  * Copyright(c) 2006-2007, Ext JS, LLC.
39737  *
39738  * Originally Released Under LGPL - original licence link has changed is not relivant.
39739  *
39740  * Fork - LGPL
39741  * <script type="text/javascript">
39742  */
39743  
39744
39745
39746 /**
39747  * @class Roo.SplitLayoutRegion
39748  * @extends Roo.LayoutRegion
39749  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39750  */
39751 Roo.bootstrap.layout.Split = function(config){
39752     this.cursor = config.cursor;
39753     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39754 };
39755
39756 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39757 {
39758     splitTip : "Drag to resize.",
39759     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39760     useSplitTips : false,
39761
39762     applyConfig : function(config){
39763         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39764     },
39765     
39766     onRender : function(ctr,pos) {
39767         
39768         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39769         if(!this.config.split){
39770             return;
39771         }
39772         if(!this.split){
39773             
39774             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39775                             tag: "div",
39776                             id: this.el.id + "-split",
39777                             cls: "roo-layout-split roo-layout-split-"+this.position,
39778                             html: "&#160;"
39779             });
39780             /** The SplitBar for this region 
39781             * @type Roo.SplitBar */
39782             // does not exist yet...
39783             Roo.log([this.position, this.orientation]);
39784             
39785             this.split = new Roo.bootstrap.SplitBar({
39786                 dragElement : splitEl,
39787                 resizingElement: this.el,
39788                 orientation : this.orientation
39789             });
39790             
39791             this.split.on("moved", this.onSplitMove, this);
39792             this.split.useShim = this.config.useShim === true;
39793             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39794             if(this.useSplitTips){
39795                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39796             }
39797             //if(config.collapsible){
39798             //    this.split.el.on("dblclick", this.collapse,  this);
39799             //}
39800         }
39801         if(typeof this.config.minSize != "undefined"){
39802             this.split.minSize = this.config.minSize;
39803         }
39804         if(typeof this.config.maxSize != "undefined"){
39805             this.split.maxSize = this.config.maxSize;
39806         }
39807         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39808             this.hideSplitter();
39809         }
39810         
39811     },
39812
39813     getHMaxSize : function(){
39814          var cmax = this.config.maxSize || 10000;
39815          var center = this.mgr.getRegion("center");
39816          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39817     },
39818
39819     getVMaxSize : function(){
39820          var cmax = this.config.maxSize || 10000;
39821          var center = this.mgr.getRegion("center");
39822          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39823     },
39824
39825     onSplitMove : function(split, newSize){
39826         this.fireEvent("resized", this, newSize);
39827     },
39828     
39829     /** 
39830      * Returns the {@link Roo.SplitBar} for this region.
39831      * @return {Roo.SplitBar}
39832      */
39833     getSplitBar : function(){
39834         return this.split;
39835     },
39836     
39837     hide : function(){
39838         this.hideSplitter();
39839         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39840     },
39841
39842     hideSplitter : function(){
39843         if(this.split){
39844             this.split.el.setLocation(-2000,-2000);
39845             this.split.el.hide();
39846         }
39847     },
39848
39849     show : function(){
39850         if(this.split){
39851             this.split.el.show();
39852         }
39853         Roo.bootstrap.layout.Split.superclass.show.call(this);
39854     },
39855     
39856     beforeSlide: function(){
39857         if(Roo.isGecko){// firefox overflow auto bug workaround
39858             this.bodyEl.clip();
39859             if(this.tabs) {
39860                 this.tabs.bodyEl.clip();
39861             }
39862             if(this.activePanel){
39863                 this.activePanel.getEl().clip();
39864                 
39865                 if(this.activePanel.beforeSlide){
39866                     this.activePanel.beforeSlide();
39867                 }
39868             }
39869         }
39870     },
39871     
39872     afterSlide : function(){
39873         if(Roo.isGecko){// firefox overflow auto bug workaround
39874             this.bodyEl.unclip();
39875             if(this.tabs) {
39876                 this.tabs.bodyEl.unclip();
39877             }
39878             if(this.activePanel){
39879                 this.activePanel.getEl().unclip();
39880                 if(this.activePanel.afterSlide){
39881                     this.activePanel.afterSlide();
39882                 }
39883             }
39884         }
39885     },
39886
39887     initAutoHide : function(){
39888         if(this.autoHide !== false){
39889             if(!this.autoHideHd){
39890                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39891                 this.autoHideHd = {
39892                     "mouseout": function(e){
39893                         if(!e.within(this.el, true)){
39894                             st.delay(500);
39895                         }
39896                     },
39897                     "mouseover" : function(e){
39898                         st.cancel();
39899                     },
39900                     scope : this
39901                 };
39902             }
39903             this.el.on(this.autoHideHd);
39904         }
39905     },
39906
39907     clearAutoHide : function(){
39908         if(this.autoHide !== false){
39909             this.el.un("mouseout", this.autoHideHd.mouseout);
39910             this.el.un("mouseover", this.autoHideHd.mouseover);
39911         }
39912     },
39913
39914     clearMonitor : function(){
39915         Roo.get(document).un("click", this.slideInIf, this);
39916     },
39917
39918     // these names are backwards but not changed for compat
39919     slideOut : function(){
39920         if(this.isSlid || this.el.hasActiveFx()){
39921             return;
39922         }
39923         this.isSlid = true;
39924         if(this.collapseBtn){
39925             this.collapseBtn.hide();
39926         }
39927         this.closeBtnState = this.closeBtn.getStyle('display');
39928         this.closeBtn.hide();
39929         if(this.stickBtn){
39930             this.stickBtn.show();
39931         }
39932         this.el.show();
39933         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39934         this.beforeSlide();
39935         this.el.setStyle("z-index", 10001);
39936         this.el.slideIn(this.getSlideAnchor(), {
39937             callback: function(){
39938                 this.afterSlide();
39939                 this.initAutoHide();
39940                 Roo.get(document).on("click", this.slideInIf, this);
39941                 this.fireEvent("slideshow", this);
39942             },
39943             scope: this,
39944             block: true
39945         });
39946     },
39947
39948     afterSlideIn : function(){
39949         this.clearAutoHide();
39950         this.isSlid = false;
39951         this.clearMonitor();
39952         this.el.setStyle("z-index", "");
39953         if(this.collapseBtn){
39954             this.collapseBtn.show();
39955         }
39956         this.closeBtn.setStyle('display', this.closeBtnState);
39957         if(this.stickBtn){
39958             this.stickBtn.hide();
39959         }
39960         this.fireEvent("slidehide", this);
39961     },
39962
39963     slideIn : function(cb){
39964         if(!this.isSlid || this.el.hasActiveFx()){
39965             Roo.callback(cb);
39966             return;
39967         }
39968         this.isSlid = false;
39969         this.beforeSlide();
39970         this.el.slideOut(this.getSlideAnchor(), {
39971             callback: function(){
39972                 this.el.setLeftTop(-10000, -10000);
39973                 this.afterSlide();
39974                 this.afterSlideIn();
39975                 Roo.callback(cb);
39976             },
39977             scope: this,
39978             block: true
39979         });
39980     },
39981     
39982     slideInIf : function(e){
39983         if(!e.within(this.el)){
39984             this.slideIn();
39985         }
39986     },
39987
39988     animateCollapse : function(){
39989         this.beforeSlide();
39990         this.el.setStyle("z-index", 20000);
39991         var anchor = this.getSlideAnchor();
39992         this.el.slideOut(anchor, {
39993             callback : function(){
39994                 this.el.setStyle("z-index", "");
39995                 this.collapsedEl.slideIn(anchor, {duration:.3});
39996                 this.afterSlide();
39997                 this.el.setLocation(-10000,-10000);
39998                 this.el.hide();
39999                 this.fireEvent("collapsed", this);
40000             },
40001             scope: this,
40002             block: true
40003         });
40004     },
40005
40006     animateExpand : function(){
40007         this.beforeSlide();
40008         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40009         this.el.setStyle("z-index", 20000);
40010         this.collapsedEl.hide({
40011             duration:.1
40012         });
40013         this.el.slideIn(this.getSlideAnchor(), {
40014             callback : function(){
40015                 this.el.setStyle("z-index", "");
40016                 this.afterSlide();
40017                 if(this.split){
40018                     this.split.el.show();
40019                 }
40020                 this.fireEvent("invalidated", this);
40021                 this.fireEvent("expanded", this);
40022             },
40023             scope: this,
40024             block: true
40025         });
40026     },
40027
40028     anchors : {
40029         "west" : "left",
40030         "east" : "right",
40031         "north" : "top",
40032         "south" : "bottom"
40033     },
40034
40035     sanchors : {
40036         "west" : "l",
40037         "east" : "r",
40038         "north" : "t",
40039         "south" : "b"
40040     },
40041
40042     canchors : {
40043         "west" : "tl-tr",
40044         "east" : "tr-tl",
40045         "north" : "tl-bl",
40046         "south" : "bl-tl"
40047     },
40048
40049     getAnchor : function(){
40050         return this.anchors[this.position];
40051     },
40052
40053     getCollapseAnchor : function(){
40054         return this.canchors[this.position];
40055     },
40056
40057     getSlideAnchor : function(){
40058         return this.sanchors[this.position];
40059     },
40060
40061     getAlignAdj : function(){
40062         var cm = this.cmargins;
40063         switch(this.position){
40064             case "west":
40065                 return [0, 0];
40066             break;
40067             case "east":
40068                 return [0, 0];
40069             break;
40070             case "north":
40071                 return [0, 0];
40072             break;
40073             case "south":
40074                 return [0, 0];
40075             break;
40076         }
40077     },
40078
40079     getExpandAdj : function(){
40080         var c = this.collapsedEl, cm = this.cmargins;
40081         switch(this.position){
40082             case "west":
40083                 return [-(cm.right+c.getWidth()+cm.left), 0];
40084             break;
40085             case "east":
40086                 return [cm.right+c.getWidth()+cm.left, 0];
40087             break;
40088             case "north":
40089                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40090             break;
40091             case "south":
40092                 return [0, cm.top+cm.bottom+c.getHeight()];
40093             break;
40094         }
40095     }
40096 });/*
40097  * Based on:
40098  * Ext JS Library 1.1.1
40099  * Copyright(c) 2006-2007, Ext JS, LLC.
40100  *
40101  * Originally Released Under LGPL - original licence link has changed is not relivant.
40102  *
40103  * Fork - LGPL
40104  * <script type="text/javascript">
40105  */
40106 /*
40107  * These classes are private internal classes
40108  */
40109 Roo.bootstrap.layout.Center = function(config){
40110     config.region = "center";
40111     Roo.bootstrap.layout.Region.call(this, config);
40112     this.visible = true;
40113     this.minWidth = config.minWidth || 20;
40114     this.minHeight = config.minHeight || 20;
40115 };
40116
40117 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40118     hide : function(){
40119         // center panel can't be hidden
40120     },
40121     
40122     show : function(){
40123         // center panel can't be hidden
40124     },
40125     
40126     getMinWidth: function(){
40127         return this.minWidth;
40128     },
40129     
40130     getMinHeight: function(){
40131         return this.minHeight;
40132     }
40133 });
40134
40135
40136
40137
40138  
40139
40140
40141
40142
40143
40144
40145 Roo.bootstrap.layout.North = function(config)
40146 {
40147     config.region = 'north';
40148     config.cursor = 'n-resize';
40149     
40150     Roo.bootstrap.layout.Split.call(this, config);
40151     
40152     
40153     if(this.split){
40154         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40155         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40156         this.split.el.addClass("roo-layout-split-v");
40157     }
40158     //var size = config.initialSize || config.height;
40159     //if(this.el && typeof size != "undefined"){
40160     //    this.el.setHeight(size);
40161     //}
40162 };
40163 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40164 {
40165     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40166      
40167      
40168     onRender : function(ctr, pos)
40169     {
40170         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40171         var size = this.config.initialSize || this.config.height;
40172         if(this.el && typeof size != "undefined"){
40173             this.el.setHeight(size);
40174         }
40175     
40176     },
40177     
40178     getBox : function(){
40179         if(this.collapsed){
40180             return this.collapsedEl.getBox();
40181         }
40182         var box = this.el.getBox();
40183         if(this.split){
40184             box.height += this.split.el.getHeight();
40185         }
40186         return box;
40187     },
40188     
40189     updateBox : function(box){
40190         if(this.split && !this.collapsed){
40191             box.height -= this.split.el.getHeight();
40192             this.split.el.setLeft(box.x);
40193             this.split.el.setTop(box.y+box.height);
40194             this.split.el.setWidth(box.width);
40195         }
40196         if(this.collapsed){
40197             this.updateBody(box.width, null);
40198         }
40199         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40200     }
40201 });
40202
40203
40204
40205
40206
40207 Roo.bootstrap.layout.South = function(config){
40208     config.region = 'south';
40209     config.cursor = 's-resize';
40210     Roo.bootstrap.layout.Split.call(this, config);
40211     if(this.split){
40212         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40213         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40214         this.split.el.addClass("roo-layout-split-v");
40215     }
40216     
40217 };
40218
40219 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40220     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40221     
40222     onRender : function(ctr, pos)
40223     {
40224         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40225         var size = this.config.initialSize || this.config.height;
40226         if(this.el && typeof size != "undefined"){
40227             this.el.setHeight(size);
40228         }
40229     
40230     },
40231     
40232     getBox : function(){
40233         if(this.collapsed){
40234             return this.collapsedEl.getBox();
40235         }
40236         var box = this.el.getBox();
40237         if(this.split){
40238             var sh = this.split.el.getHeight();
40239             box.height += sh;
40240             box.y -= sh;
40241         }
40242         return box;
40243     },
40244     
40245     updateBox : function(box){
40246         if(this.split && !this.collapsed){
40247             var sh = this.split.el.getHeight();
40248             box.height -= sh;
40249             box.y += sh;
40250             this.split.el.setLeft(box.x);
40251             this.split.el.setTop(box.y-sh);
40252             this.split.el.setWidth(box.width);
40253         }
40254         if(this.collapsed){
40255             this.updateBody(box.width, null);
40256         }
40257         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40258     }
40259 });
40260
40261 Roo.bootstrap.layout.East = function(config){
40262     config.region = "east";
40263     config.cursor = "e-resize";
40264     Roo.bootstrap.layout.Split.call(this, config);
40265     if(this.split){
40266         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40267         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40268         this.split.el.addClass("roo-layout-split-h");
40269     }
40270     
40271 };
40272 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40273     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40274     
40275     onRender : function(ctr, pos)
40276     {
40277         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40278         var size = this.config.initialSize || this.config.width;
40279         if(this.el && typeof size != "undefined"){
40280             this.el.setWidth(size);
40281         }
40282     
40283     },
40284     
40285     getBox : function(){
40286         if(this.collapsed){
40287             return this.collapsedEl.getBox();
40288         }
40289         var box = this.el.getBox();
40290         if(this.split){
40291             var sw = this.split.el.getWidth();
40292             box.width += sw;
40293             box.x -= sw;
40294         }
40295         return box;
40296     },
40297
40298     updateBox : function(box){
40299         if(this.split && !this.collapsed){
40300             var sw = this.split.el.getWidth();
40301             box.width -= sw;
40302             this.split.el.setLeft(box.x);
40303             this.split.el.setTop(box.y);
40304             this.split.el.setHeight(box.height);
40305             box.x += sw;
40306         }
40307         if(this.collapsed){
40308             this.updateBody(null, box.height);
40309         }
40310         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40311     }
40312 });
40313
40314 Roo.bootstrap.layout.West = function(config){
40315     config.region = "west";
40316     config.cursor = "w-resize";
40317     
40318     Roo.bootstrap.layout.Split.call(this, config);
40319     if(this.split){
40320         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40321         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40322         this.split.el.addClass("roo-layout-split-h");
40323     }
40324     
40325 };
40326 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40327     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40328     
40329     onRender: function(ctr, pos)
40330     {
40331         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40332         var size = this.config.initialSize || this.config.width;
40333         if(typeof size != "undefined"){
40334             this.el.setWidth(size);
40335         }
40336     },
40337     
40338     getBox : function(){
40339         if(this.collapsed){
40340             return this.collapsedEl.getBox();
40341         }
40342         var box = this.el.getBox();
40343         if (box.width == 0) {
40344             box.width = this.config.width; // kludge?
40345         }
40346         if(this.split){
40347             box.width += this.split.el.getWidth();
40348         }
40349         return box;
40350     },
40351     
40352     updateBox : function(box){
40353         if(this.split && !this.collapsed){
40354             var sw = this.split.el.getWidth();
40355             box.width -= sw;
40356             this.split.el.setLeft(box.x+box.width);
40357             this.split.el.setTop(box.y);
40358             this.split.el.setHeight(box.height);
40359         }
40360         if(this.collapsed){
40361             this.updateBody(null, box.height);
40362         }
40363         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40364     }
40365 });Roo.namespace("Roo.bootstrap.panel");/*
40366  * Based on:
40367  * Ext JS Library 1.1.1
40368  * Copyright(c) 2006-2007, Ext JS, LLC.
40369  *
40370  * Originally Released Under LGPL - original licence link has changed is not relivant.
40371  *
40372  * Fork - LGPL
40373  * <script type="text/javascript">
40374  */
40375 /**
40376  * @class Roo.ContentPanel
40377  * @extends Roo.util.Observable
40378  * A basic ContentPanel element.
40379  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40380  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40381  * @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
40382  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40383  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40384  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40385  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40386  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40387  * @cfg {String} title          The title for this panel
40388  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40389  * @cfg {String} url            Calls {@link #setUrl} with this value
40390  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40391  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40392  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40393  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40394  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40395  * @cfg {Boolean} badges render the badges
40396  * @cfg {String} cls  extra classes to use  
40397  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40398
40399  * @constructor
40400  * Create a new ContentPanel.
40401  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40402  * @param {String/Object} config A string to set only the title or a config object
40403  * @param {String} content (optional) Set the HTML content for this panel
40404  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40405  */
40406 Roo.bootstrap.panel.Content = function( config){
40407     
40408     this.tpl = config.tpl || false;
40409     
40410     var el = config.el;
40411     var content = config.content;
40412
40413     if(config.autoCreate){ // xtype is available if this is called from factory
40414         el = Roo.id();
40415     }
40416     this.el = Roo.get(el);
40417     if(!this.el && config && config.autoCreate){
40418         if(typeof config.autoCreate == "object"){
40419             if(!config.autoCreate.id){
40420                 config.autoCreate.id = config.id||el;
40421             }
40422             this.el = Roo.DomHelper.append(document.body,
40423                         config.autoCreate, true);
40424         }else{
40425             var elcfg =  {
40426                 tag: "div",
40427                 cls: (config.cls || '') +
40428                     (config.background ? ' bg-' + config.background : '') +
40429                     " roo-layout-inactive-content",
40430                 id: config.id||el
40431             };
40432             if (config.iframe) {
40433                 elcfg.cn = [
40434                     {
40435                         tag : 'iframe',
40436                         style : 'border: 0px',
40437                         src : 'about:blank'
40438                     }
40439                 ];
40440             }
40441               
40442             if (config.html) {
40443                 elcfg.html = config.html;
40444                 
40445             }
40446                         
40447             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40448             if (config.iframe) {
40449                 this.iframeEl = this.el.select('iframe',true).first();
40450             }
40451             
40452         }
40453     } 
40454     this.closable = false;
40455     this.loaded = false;
40456     this.active = false;
40457    
40458       
40459     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40460         
40461         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40462         
40463         this.wrapEl = this.el; //this.el.wrap();
40464         var ti = [];
40465         if (config.toolbar.items) {
40466             ti = config.toolbar.items ;
40467             delete config.toolbar.items ;
40468         }
40469         
40470         var nitems = [];
40471         this.toolbar.render(this.wrapEl, 'before');
40472         for(var i =0;i < ti.length;i++) {
40473           //  Roo.log(['add child', items[i]]);
40474             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40475         }
40476         this.toolbar.items = nitems;
40477         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40478         delete config.toolbar;
40479         
40480     }
40481     /*
40482     // xtype created footer. - not sure if will work as we normally have to render first..
40483     if (this.footer && !this.footer.el && this.footer.xtype) {
40484         if (!this.wrapEl) {
40485             this.wrapEl = this.el.wrap();
40486         }
40487     
40488         this.footer.container = this.wrapEl.createChild();
40489          
40490         this.footer = Roo.factory(this.footer, Roo);
40491         
40492     }
40493     */
40494     
40495      if(typeof config == "string"){
40496         this.title = config;
40497     }else{
40498         Roo.apply(this, config);
40499     }
40500     
40501     if(this.resizeEl){
40502         this.resizeEl = Roo.get(this.resizeEl, true);
40503     }else{
40504         this.resizeEl = this.el;
40505     }
40506     // handle view.xtype
40507     
40508  
40509     
40510     
40511     this.addEvents({
40512         /**
40513          * @event activate
40514          * Fires when this panel is activated. 
40515          * @param {Roo.ContentPanel} this
40516          */
40517         "activate" : true,
40518         /**
40519          * @event deactivate
40520          * Fires when this panel is activated. 
40521          * @param {Roo.ContentPanel} this
40522          */
40523         "deactivate" : true,
40524
40525         /**
40526          * @event resize
40527          * Fires when this panel is resized if fitToFrame is true.
40528          * @param {Roo.ContentPanel} this
40529          * @param {Number} width The width after any component adjustments
40530          * @param {Number} height The height after any component adjustments
40531          */
40532         "resize" : true,
40533         
40534          /**
40535          * @event render
40536          * Fires when this tab is created
40537          * @param {Roo.ContentPanel} this
40538          */
40539         "render" : true,
40540         
40541           /**
40542          * @event scroll
40543          * Fires when this content is scrolled
40544          * @param {Roo.ContentPanel} this
40545          * @param {Event} scrollEvent
40546          */
40547         "scroll" : true
40548         
40549         
40550         
40551     });
40552     
40553
40554     
40555     
40556     if(this.autoScroll && !this.iframe){
40557         this.resizeEl.setStyle("overflow", "auto");
40558         this.resizeEl.on('scroll', this.onScroll, this);
40559     } else {
40560         // fix randome scrolling
40561         //this.el.on('scroll', function() {
40562         //    Roo.log('fix random scolling');
40563         //    this.scrollTo('top',0); 
40564         //});
40565     }
40566     content = content || this.content;
40567     if(content){
40568         this.setContent(content);
40569     }
40570     if(config && config.url){
40571         this.setUrl(this.url, this.params, this.loadOnce);
40572     }
40573     
40574     
40575     
40576     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40577     
40578     if (this.view && typeof(this.view.xtype) != 'undefined') {
40579         this.view.el = this.el.appendChild(document.createElement("div"));
40580         this.view = Roo.factory(this.view); 
40581         this.view.render  &&  this.view.render(false, '');  
40582     }
40583     
40584     
40585     this.fireEvent('render', this);
40586 };
40587
40588 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40589     
40590     cls : '',
40591     background : '',
40592     
40593     tabTip : '',
40594     
40595     iframe : false,
40596     iframeEl : false,
40597     
40598     /* Resize Element - use this to work out scroll etc. */
40599     resizeEl : false,
40600     
40601     setRegion : function(region){
40602         this.region = region;
40603         this.setActiveClass(region && !this.background);
40604     },
40605     
40606     
40607     setActiveClass: function(state)
40608     {
40609         if(state){
40610            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40611            this.el.setStyle('position','relative');
40612         }else{
40613            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40614            this.el.setStyle('position', 'absolute');
40615         } 
40616     },
40617     
40618     /**
40619      * Returns the toolbar for this Panel if one was configured. 
40620      * @return {Roo.Toolbar} 
40621      */
40622     getToolbar : function(){
40623         return this.toolbar;
40624     },
40625     
40626     setActiveState : function(active)
40627     {
40628         this.active = active;
40629         this.setActiveClass(active);
40630         if(!active){
40631             if(this.fireEvent("deactivate", this) === false){
40632                 return false;
40633             }
40634             return true;
40635         }
40636         this.fireEvent("activate", this);
40637         return true;
40638     },
40639     /**
40640      * Updates this panel's element (not for iframe)
40641      * @param {String} content The new content
40642      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40643     */
40644     setContent : function(content, loadScripts){
40645         if (this.iframe) {
40646             return;
40647         }
40648         
40649         this.el.update(content, loadScripts);
40650     },
40651
40652     ignoreResize : function(w, h){
40653         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40654             return true;
40655         }else{
40656             this.lastSize = {width: w, height: h};
40657             return false;
40658         }
40659     },
40660     /**
40661      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40662      * @return {Roo.UpdateManager} The UpdateManager
40663      */
40664     getUpdateManager : function(){
40665         if (this.iframe) {
40666             return false;
40667         }
40668         return this.el.getUpdateManager();
40669     },
40670      /**
40671      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40672      * Does not work with IFRAME contents
40673      * @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:
40674 <pre><code>
40675 panel.load({
40676     url: "your-url.php",
40677     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40678     callback: yourFunction,
40679     scope: yourObject, //(optional scope)
40680     discardUrl: false,
40681     nocache: false,
40682     text: "Loading...",
40683     timeout: 30,
40684     scripts: false
40685 });
40686 </code></pre>
40687      
40688      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40689      * 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.
40690      * @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}
40691      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40692      * @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.
40693      * @return {Roo.ContentPanel} this
40694      */
40695     load : function(){
40696         
40697         if (this.iframe) {
40698             return this;
40699         }
40700         
40701         var um = this.el.getUpdateManager();
40702         um.update.apply(um, arguments);
40703         return this;
40704     },
40705
40706
40707     /**
40708      * 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.
40709      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40710      * @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)
40711      * @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)
40712      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40713      */
40714     setUrl : function(url, params, loadOnce){
40715         if (this.iframe) {
40716             this.iframeEl.dom.src = url;
40717             return false;
40718         }
40719         
40720         if(this.refreshDelegate){
40721             this.removeListener("activate", this.refreshDelegate);
40722         }
40723         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40724         this.on("activate", this.refreshDelegate);
40725         return this.el.getUpdateManager();
40726     },
40727     
40728     _handleRefresh : function(url, params, loadOnce){
40729         if(!loadOnce || !this.loaded){
40730             var updater = this.el.getUpdateManager();
40731             updater.update(url, params, this._setLoaded.createDelegate(this));
40732         }
40733     },
40734     
40735     _setLoaded : function(){
40736         this.loaded = true;
40737     }, 
40738     
40739     /**
40740      * Returns this panel's id
40741      * @return {String} 
40742      */
40743     getId : function(){
40744         return this.el.id;
40745     },
40746     
40747     /** 
40748      * Returns this panel's element - used by regiosn to add.
40749      * @return {Roo.Element} 
40750      */
40751     getEl : function(){
40752         return this.wrapEl || this.el;
40753     },
40754     
40755    
40756     
40757     adjustForComponents : function(width, height)
40758     {
40759         //Roo.log('adjustForComponents ');
40760         if(this.resizeEl != this.el){
40761             width -= this.el.getFrameWidth('lr');
40762             height -= this.el.getFrameWidth('tb');
40763         }
40764         if(this.toolbar){
40765             var te = this.toolbar.getEl();
40766             te.setWidth(width);
40767             height -= te.getHeight();
40768         }
40769         if(this.footer){
40770             var te = this.footer.getEl();
40771             te.setWidth(width);
40772             height -= te.getHeight();
40773         }
40774         
40775         
40776         if(this.adjustments){
40777             width += this.adjustments[0];
40778             height += this.adjustments[1];
40779         }
40780         return {"width": width, "height": height};
40781     },
40782     
40783     setSize : function(width, height){
40784         if(this.fitToFrame && !this.ignoreResize(width, height)){
40785             if(this.fitContainer && this.resizeEl != this.el){
40786                 this.el.setSize(width, height);
40787             }
40788             var size = this.adjustForComponents(width, height);
40789             if (this.iframe) {
40790                 this.iframeEl.setSize(width,height);
40791             }
40792             
40793             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40794             this.fireEvent('resize', this, size.width, size.height);
40795             
40796             
40797         }
40798     },
40799     
40800     /**
40801      * Returns this panel's title
40802      * @return {String} 
40803      */
40804     getTitle : function(){
40805         
40806         if (typeof(this.title) != 'object') {
40807             return this.title;
40808         }
40809         
40810         var t = '';
40811         for (var k in this.title) {
40812             if (!this.title.hasOwnProperty(k)) {
40813                 continue;
40814             }
40815             
40816             if (k.indexOf('-') >= 0) {
40817                 var s = k.split('-');
40818                 for (var i = 0; i<s.length; i++) {
40819                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40820                 }
40821             } else {
40822                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40823             }
40824         }
40825         return t;
40826     },
40827     
40828     /**
40829      * Set this panel's title
40830      * @param {String} title
40831      */
40832     setTitle : function(title){
40833         this.title = title;
40834         if(this.region){
40835             this.region.updatePanelTitle(this, title);
40836         }
40837     },
40838     
40839     /**
40840      * Returns true is this panel was configured to be closable
40841      * @return {Boolean} 
40842      */
40843     isClosable : function(){
40844         return this.closable;
40845     },
40846     
40847     beforeSlide : function(){
40848         this.el.clip();
40849         this.resizeEl.clip();
40850     },
40851     
40852     afterSlide : function(){
40853         this.el.unclip();
40854         this.resizeEl.unclip();
40855     },
40856     
40857     /**
40858      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40859      *   Will fail silently if the {@link #setUrl} method has not been called.
40860      *   This does not activate the panel, just updates its content.
40861      */
40862     refresh : function(){
40863         if(this.refreshDelegate){
40864            this.loaded = false;
40865            this.refreshDelegate();
40866         }
40867     },
40868     
40869     /**
40870      * Destroys this panel
40871      */
40872     destroy : function(){
40873         this.el.removeAllListeners();
40874         var tempEl = document.createElement("span");
40875         tempEl.appendChild(this.el.dom);
40876         tempEl.innerHTML = "";
40877         this.el.remove();
40878         this.el = null;
40879     },
40880     
40881     /**
40882      * form - if the content panel contains a form - this is a reference to it.
40883      * @type {Roo.form.Form}
40884      */
40885     form : false,
40886     /**
40887      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40888      *    This contains a reference to it.
40889      * @type {Roo.View}
40890      */
40891     view : false,
40892     
40893       /**
40894      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40895      * <pre><code>
40896
40897 layout.addxtype({
40898        xtype : 'Form',
40899        items: [ .... ]
40900    }
40901 );
40902
40903 </code></pre>
40904      * @param {Object} cfg Xtype definition of item to add.
40905      */
40906     
40907     
40908     getChildContainer: function () {
40909         return this.getEl();
40910     },
40911     
40912     
40913     onScroll : function(e)
40914     {
40915         this.fireEvent('scroll', this, e);
40916     }
40917     
40918     
40919     /*
40920         var  ret = new Roo.factory(cfg);
40921         return ret;
40922         
40923         
40924         // add form..
40925         if (cfg.xtype.match(/^Form$/)) {
40926             
40927             var el;
40928             //if (this.footer) {
40929             //    el = this.footer.container.insertSibling(false, 'before');
40930             //} else {
40931                 el = this.el.createChild();
40932             //}
40933
40934             this.form = new  Roo.form.Form(cfg);
40935             
40936             
40937             if ( this.form.allItems.length) {
40938                 this.form.render(el.dom);
40939             }
40940             return this.form;
40941         }
40942         // should only have one of theses..
40943         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40944             // views.. should not be just added - used named prop 'view''
40945             
40946             cfg.el = this.el.appendChild(document.createElement("div"));
40947             // factory?
40948             
40949             var ret = new Roo.factory(cfg);
40950              
40951              ret.render && ret.render(false, ''); // render blank..
40952             this.view = ret;
40953             return ret;
40954         }
40955         return false;
40956     }
40957     \*/
40958 });
40959  
40960 /**
40961  * @class Roo.bootstrap.panel.Grid
40962  * @extends Roo.bootstrap.panel.Content
40963  * @constructor
40964  * Create a new GridPanel.
40965  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40966  * @param {Object} config A the config object
40967   
40968  */
40969
40970
40971
40972 Roo.bootstrap.panel.Grid = function(config)
40973 {
40974     
40975       
40976     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40977         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40978
40979     config.el = this.wrapper;
40980     //this.el = this.wrapper;
40981     
40982       if (config.container) {
40983         // ctor'ed from a Border/panel.grid
40984         
40985         
40986         this.wrapper.setStyle("overflow", "hidden");
40987         this.wrapper.addClass('roo-grid-container');
40988
40989     }
40990     
40991     
40992     if(config.toolbar){
40993         var tool_el = this.wrapper.createChild();    
40994         this.toolbar = Roo.factory(config.toolbar);
40995         var ti = [];
40996         if (config.toolbar.items) {
40997             ti = config.toolbar.items ;
40998             delete config.toolbar.items ;
40999         }
41000         
41001         var nitems = [];
41002         this.toolbar.render(tool_el);
41003         for(var i =0;i < ti.length;i++) {
41004           //  Roo.log(['add child', items[i]]);
41005             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41006         }
41007         this.toolbar.items = nitems;
41008         
41009         delete config.toolbar;
41010     }
41011     
41012     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41013     config.grid.scrollBody = true;;
41014     config.grid.monitorWindowResize = false; // turn off autosizing
41015     config.grid.autoHeight = false;
41016     config.grid.autoWidth = false;
41017     
41018     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41019     
41020     if (config.background) {
41021         // render grid on panel activation (if panel background)
41022         this.on('activate', function(gp) {
41023             if (!gp.grid.rendered) {
41024                 gp.grid.render(this.wrapper);
41025                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41026             }
41027         });
41028             
41029     } else {
41030         this.grid.render(this.wrapper);
41031         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41032
41033     }
41034     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41035     // ??? needed ??? config.el = this.wrapper;
41036     
41037     
41038     
41039   
41040     // xtype created footer. - not sure if will work as we normally have to render first..
41041     if (this.footer && !this.footer.el && this.footer.xtype) {
41042         
41043         var ctr = this.grid.getView().getFooterPanel(true);
41044         this.footer.dataSource = this.grid.dataSource;
41045         this.footer = Roo.factory(this.footer, Roo);
41046         this.footer.render(ctr);
41047         
41048     }
41049     
41050     
41051     
41052     
41053      
41054 };
41055
41056 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41057     getId : function(){
41058         return this.grid.id;
41059     },
41060     
41061     /**
41062      * Returns the grid for this panel
41063      * @return {Roo.bootstrap.Table} 
41064      */
41065     getGrid : function(){
41066         return this.grid;    
41067     },
41068     
41069     setSize : function(width, height){
41070         if(!this.ignoreResize(width, height)){
41071             var grid = this.grid;
41072             var size = this.adjustForComponents(width, height);
41073             // tfoot is not a footer?
41074           
41075             
41076             var gridel = grid.getGridEl();
41077             gridel.setSize(size.width, size.height);
41078             
41079             var tbd = grid.getGridEl().select('tbody', true).first();
41080             var thd = grid.getGridEl().select('thead',true).first();
41081             var tbf= grid.getGridEl().select('tfoot', true).first();
41082
41083             if (tbf) {
41084                 size.height -= tbf.getHeight();
41085             }
41086             if (thd) {
41087                 size.height -= thd.getHeight();
41088             }
41089             
41090             tbd.setSize(size.width, size.height );
41091             // this is for the account management tab -seems to work there.
41092             var thd = grid.getGridEl().select('thead',true).first();
41093             //if (tbd) {
41094             //    tbd.setSize(size.width, size.height - thd.getHeight());
41095             //}
41096              
41097             grid.autoSize();
41098         }
41099     },
41100      
41101     
41102     
41103     beforeSlide : function(){
41104         this.grid.getView().scroller.clip();
41105     },
41106     
41107     afterSlide : function(){
41108         this.grid.getView().scroller.unclip();
41109     },
41110     
41111     destroy : function(){
41112         this.grid.destroy();
41113         delete this.grid;
41114         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41115     }
41116 });
41117
41118 /**
41119  * @class Roo.bootstrap.panel.Nest
41120  * @extends Roo.bootstrap.panel.Content
41121  * @constructor
41122  * Create a new Panel, that can contain a layout.Border.
41123  * 
41124  * 
41125  * @param {Roo.BorderLayout} layout The layout for this panel
41126  * @param {String/Object} config A string to set only the title or a config object
41127  */
41128 Roo.bootstrap.panel.Nest = function(config)
41129 {
41130     // construct with only one argument..
41131     /* FIXME - implement nicer consturctors
41132     if (layout.layout) {
41133         config = layout;
41134         layout = config.layout;
41135         delete config.layout;
41136     }
41137     if (layout.xtype && !layout.getEl) {
41138         // then layout needs constructing..
41139         layout = Roo.factory(layout, Roo);
41140     }
41141     */
41142     
41143     config.el =  config.layout.getEl();
41144     
41145     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41146     
41147     config.layout.monitorWindowResize = false; // turn off autosizing
41148     this.layout = config.layout;
41149     this.layout.getEl().addClass("roo-layout-nested-layout");
41150     this.layout.parent = this;
41151     
41152     
41153     
41154     
41155 };
41156
41157 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41158
41159     setSize : function(width, height){
41160         if(!this.ignoreResize(width, height)){
41161             var size = this.adjustForComponents(width, height);
41162             var el = this.layout.getEl();
41163             if (size.height < 1) {
41164                 el.setWidth(size.width);   
41165             } else {
41166                 el.setSize(size.width, size.height);
41167             }
41168             var touch = el.dom.offsetWidth;
41169             this.layout.layout();
41170             // ie requires a double layout on the first pass
41171             if(Roo.isIE && !this.initialized){
41172                 this.initialized = true;
41173                 this.layout.layout();
41174             }
41175         }
41176     },
41177     
41178     // activate all subpanels if not currently active..
41179     
41180     setActiveState : function(active){
41181         this.active = active;
41182         this.setActiveClass(active);
41183         
41184         if(!active){
41185             this.fireEvent("deactivate", this);
41186             return;
41187         }
41188         
41189         this.fireEvent("activate", this);
41190         // not sure if this should happen before or after..
41191         if (!this.layout) {
41192             return; // should not happen..
41193         }
41194         var reg = false;
41195         for (var r in this.layout.regions) {
41196             reg = this.layout.getRegion(r);
41197             if (reg.getActivePanel()) {
41198                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41199                 reg.setActivePanel(reg.getActivePanel());
41200                 continue;
41201             }
41202             if (!reg.panels.length) {
41203                 continue;
41204             }
41205             reg.showPanel(reg.getPanel(0));
41206         }
41207         
41208         
41209         
41210         
41211     },
41212     
41213     /**
41214      * Returns the nested BorderLayout for this panel
41215      * @return {Roo.BorderLayout} 
41216      */
41217     getLayout : function(){
41218         return this.layout;
41219     },
41220     
41221      /**
41222      * Adds a xtype elements to the layout of the nested panel
41223      * <pre><code>
41224
41225 panel.addxtype({
41226        xtype : 'ContentPanel',
41227        region: 'west',
41228        items: [ .... ]
41229    }
41230 );
41231
41232 panel.addxtype({
41233         xtype : 'NestedLayoutPanel',
41234         region: 'west',
41235         layout: {
41236            center: { },
41237            west: { }   
41238         },
41239         items : [ ... list of content panels or nested layout panels.. ]
41240    }
41241 );
41242 </code></pre>
41243      * @param {Object} cfg Xtype definition of item to add.
41244      */
41245     addxtype : function(cfg) {
41246         return this.layout.addxtype(cfg);
41247     
41248     }
41249 });/*
41250  * Based on:
41251  * Ext JS Library 1.1.1
41252  * Copyright(c) 2006-2007, Ext JS, LLC.
41253  *
41254  * Originally Released Under LGPL - original licence link has changed is not relivant.
41255  *
41256  * Fork - LGPL
41257  * <script type="text/javascript">
41258  */
41259 /**
41260  * @class Roo.TabPanel
41261  * @extends Roo.util.Observable
41262  * A lightweight tab container.
41263  * <br><br>
41264  * Usage:
41265  * <pre><code>
41266 // basic tabs 1, built from existing content
41267 var tabs = new Roo.TabPanel("tabs1");
41268 tabs.addTab("script", "View Script");
41269 tabs.addTab("markup", "View Markup");
41270 tabs.activate("script");
41271
41272 // more advanced tabs, built from javascript
41273 var jtabs = new Roo.TabPanel("jtabs");
41274 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41275
41276 // set up the UpdateManager
41277 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41278 var updater = tab2.getUpdateManager();
41279 updater.setDefaultUrl("ajax1.htm");
41280 tab2.on('activate', updater.refresh, updater, true);
41281
41282 // Use setUrl for Ajax loading
41283 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41284 tab3.setUrl("ajax2.htm", null, true);
41285
41286 // Disabled tab
41287 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41288 tab4.disable();
41289
41290 jtabs.activate("jtabs-1");
41291  * </code></pre>
41292  * @constructor
41293  * Create a new TabPanel.
41294  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41295  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41296  */
41297 Roo.bootstrap.panel.Tabs = function(config){
41298     /**
41299     * The container element for this TabPanel.
41300     * @type Roo.Element
41301     */
41302     this.el = Roo.get(config.el);
41303     delete config.el;
41304     if(config){
41305         if(typeof config == "boolean"){
41306             this.tabPosition = config ? "bottom" : "top";
41307         }else{
41308             Roo.apply(this, config);
41309         }
41310     }
41311     
41312     if(this.tabPosition == "bottom"){
41313         // if tabs are at the bottom = create the body first.
41314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41315         this.el.addClass("roo-tabs-bottom");
41316     }
41317     // next create the tabs holders
41318     
41319     if (this.tabPosition == "west"){
41320         
41321         var reg = this.region; // fake it..
41322         while (reg) {
41323             if (!reg.mgr.parent) {
41324                 break;
41325             }
41326             reg = reg.mgr.parent.region;
41327         }
41328         Roo.log("got nest?");
41329         Roo.log(reg);
41330         if (reg.mgr.getRegion('west')) {
41331             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41332             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41333             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41334             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41335             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41336         
41337             
41338         }
41339         
41340         
41341     } else {
41342      
41343         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41344         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41345         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41346         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41347     }
41348     
41349     
41350     if(Roo.isIE){
41351         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41352     }
41353     
41354     // finally - if tabs are at the top, then create the body last..
41355     if(this.tabPosition != "bottom"){
41356         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41357          * @type Roo.Element
41358          */
41359         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41360         this.el.addClass("roo-tabs-top");
41361     }
41362     this.items = [];
41363
41364     this.bodyEl.setStyle("position", "relative");
41365
41366     this.active = null;
41367     this.activateDelegate = this.activate.createDelegate(this);
41368
41369     this.addEvents({
41370         /**
41371          * @event tabchange
41372          * Fires when the active tab changes
41373          * @param {Roo.TabPanel} this
41374          * @param {Roo.TabPanelItem} activePanel The new active tab
41375          */
41376         "tabchange": true,
41377         /**
41378          * @event beforetabchange
41379          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41380          * @param {Roo.TabPanel} this
41381          * @param {Object} e Set cancel to true on this object to cancel the tab change
41382          * @param {Roo.TabPanelItem} tab The tab being changed to
41383          */
41384         "beforetabchange" : true
41385     });
41386
41387     Roo.EventManager.onWindowResize(this.onResize, this);
41388     this.cpad = this.el.getPadding("lr");
41389     this.hiddenCount = 0;
41390
41391
41392     // toolbar on the tabbar support...
41393     if (this.toolbar) {
41394         alert("no toolbar support yet");
41395         this.toolbar  = false;
41396         /*
41397         var tcfg = this.toolbar;
41398         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41399         this.toolbar = new Roo.Toolbar(tcfg);
41400         if (Roo.isSafari) {
41401             var tbl = tcfg.container.child('table', true);
41402             tbl.setAttribute('width', '100%');
41403         }
41404         */
41405         
41406     }
41407    
41408
41409
41410     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41411 };
41412
41413 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41414     /*
41415      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41416      */
41417     tabPosition : "top",
41418     /*
41419      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41420      */
41421     currentTabWidth : 0,
41422     /*
41423      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41424      */
41425     minTabWidth : 40,
41426     /*
41427      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41428      */
41429     maxTabWidth : 250,
41430     /*
41431      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41432      */
41433     preferredTabWidth : 175,
41434     /*
41435      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41436      */
41437     resizeTabs : false,
41438     /*
41439      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41440      */
41441     monitorResize : true,
41442     /*
41443      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41444      */
41445     toolbar : false,  // set by caller..
41446     
41447     region : false, /// set by caller
41448     
41449     disableTooltips : true, // not used yet...
41450
41451     /**
41452      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41453      * @param {String} id The id of the div to use <b>or create</b>
41454      * @param {String} text The text for the tab
41455      * @param {String} content (optional) Content to put in the TabPanelItem body
41456      * @param {Boolean} closable (optional) True to create a close icon on the tab
41457      * @return {Roo.TabPanelItem} The created TabPanelItem
41458      */
41459     addTab : function(id, text, content, closable, tpl)
41460     {
41461         var item = new Roo.bootstrap.panel.TabItem({
41462             panel: this,
41463             id : id,
41464             text : text,
41465             closable : closable,
41466             tpl : tpl
41467         });
41468         this.addTabItem(item);
41469         if(content){
41470             item.setContent(content);
41471         }
41472         return item;
41473     },
41474
41475     /**
41476      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41477      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41478      * @return {Roo.TabPanelItem}
41479      */
41480     getTab : function(id){
41481         return this.items[id];
41482     },
41483
41484     /**
41485      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41486      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41487      */
41488     hideTab : function(id){
41489         var t = this.items[id];
41490         if(!t.isHidden()){
41491            t.setHidden(true);
41492            this.hiddenCount++;
41493            this.autoSizeTabs();
41494         }
41495     },
41496
41497     /**
41498      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41499      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41500      */
41501     unhideTab : function(id){
41502         var t = this.items[id];
41503         if(t.isHidden()){
41504            t.setHidden(false);
41505            this.hiddenCount--;
41506            this.autoSizeTabs();
41507         }
41508     },
41509
41510     /**
41511      * Adds an existing {@link Roo.TabPanelItem}.
41512      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41513      */
41514     addTabItem : function(item)
41515     {
41516         this.items[item.id] = item;
41517         this.items.push(item);
41518         this.autoSizeTabs();
41519       //  if(this.resizeTabs){
41520     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41521   //         this.autoSizeTabs();
41522 //        }else{
41523 //            item.autoSize();
41524        // }
41525     },
41526
41527     /**
41528      * Removes a {@link Roo.TabPanelItem}.
41529      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41530      */
41531     removeTab : function(id){
41532         var items = this.items;
41533         var tab = items[id];
41534         if(!tab) { return; }
41535         var index = items.indexOf(tab);
41536         if(this.active == tab && items.length > 1){
41537             var newTab = this.getNextAvailable(index);
41538             if(newTab) {
41539                 newTab.activate();
41540             }
41541         }
41542         this.stripEl.dom.removeChild(tab.pnode.dom);
41543         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41544             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41545         }
41546         items.splice(index, 1);
41547         delete this.items[tab.id];
41548         tab.fireEvent("close", tab);
41549         tab.purgeListeners();
41550         this.autoSizeTabs();
41551     },
41552
41553     getNextAvailable : function(start){
41554         var items = this.items;
41555         var index = start;
41556         // look for a next tab that will slide over to
41557         // replace the one being removed
41558         while(index < items.length){
41559             var item = items[++index];
41560             if(item && !item.isHidden()){
41561                 return item;
41562             }
41563         }
41564         // if one isn't found select the previous tab (on the left)
41565         index = start;
41566         while(index >= 0){
41567             var item = items[--index];
41568             if(item && !item.isHidden()){
41569                 return item;
41570             }
41571         }
41572         return null;
41573     },
41574
41575     /**
41576      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41577      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41578      */
41579     disableTab : function(id){
41580         var tab = this.items[id];
41581         if(tab && this.active != tab){
41582             tab.disable();
41583         }
41584     },
41585
41586     /**
41587      * Enables a {@link Roo.TabPanelItem} that is disabled.
41588      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41589      */
41590     enableTab : function(id){
41591         var tab = this.items[id];
41592         tab.enable();
41593     },
41594
41595     /**
41596      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41597      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41598      * @return {Roo.TabPanelItem} The TabPanelItem.
41599      */
41600     activate : function(id)
41601     {
41602         //Roo.log('activite:'  + id);
41603         
41604         var tab = this.items[id];
41605         if(!tab){
41606             return null;
41607         }
41608         if(tab == this.active || tab.disabled){
41609             return tab;
41610         }
41611         var e = {};
41612         this.fireEvent("beforetabchange", this, e, tab);
41613         if(e.cancel !== true && !tab.disabled){
41614             if(this.active){
41615                 this.active.hide();
41616             }
41617             this.active = this.items[id];
41618             this.active.show();
41619             this.fireEvent("tabchange", this, this.active);
41620         }
41621         return tab;
41622     },
41623
41624     /**
41625      * Gets the active {@link Roo.TabPanelItem}.
41626      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41627      */
41628     getActiveTab : function(){
41629         return this.active;
41630     },
41631
41632     /**
41633      * Updates the tab body element to fit the height of the container element
41634      * for overflow scrolling
41635      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41636      */
41637     syncHeight : function(targetHeight){
41638         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41639         var bm = this.bodyEl.getMargins();
41640         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41641         this.bodyEl.setHeight(newHeight);
41642         return newHeight;
41643     },
41644
41645     onResize : function(){
41646         if(this.monitorResize){
41647             this.autoSizeTabs();
41648         }
41649     },
41650
41651     /**
41652      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41653      */
41654     beginUpdate : function(){
41655         this.updating = true;
41656     },
41657
41658     /**
41659      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41660      */
41661     endUpdate : function(){
41662         this.updating = false;
41663         this.autoSizeTabs();
41664     },
41665
41666     /**
41667      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41668      */
41669     autoSizeTabs : function()
41670     {
41671         var count = this.items.length;
41672         var vcount = count - this.hiddenCount;
41673         
41674         if (vcount < 2) {
41675             this.stripEl.hide();
41676         } else {
41677             this.stripEl.show();
41678         }
41679         
41680         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41681             return;
41682         }
41683         
41684         
41685         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41686         var availWidth = Math.floor(w / vcount);
41687         var b = this.stripBody;
41688         if(b.getWidth() > w){
41689             var tabs = this.items;
41690             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41691             if(availWidth < this.minTabWidth){
41692                 /*if(!this.sleft){    // incomplete scrolling code
41693                     this.createScrollButtons();
41694                 }
41695                 this.showScroll();
41696                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41697             }
41698         }else{
41699             if(this.currentTabWidth < this.preferredTabWidth){
41700                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41701             }
41702         }
41703     },
41704
41705     /**
41706      * Returns the number of tabs in this TabPanel.
41707      * @return {Number}
41708      */
41709      getCount : function(){
41710          return this.items.length;
41711      },
41712
41713     /**
41714      * Resizes all the tabs to the passed width
41715      * @param {Number} The new width
41716      */
41717     setTabWidth : function(width){
41718         this.currentTabWidth = width;
41719         for(var i = 0, len = this.items.length; i < len; i++) {
41720                 if(!this.items[i].isHidden()) {
41721                 this.items[i].setWidth(width);
41722             }
41723         }
41724     },
41725
41726     /**
41727      * Destroys this TabPanel
41728      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41729      */
41730     destroy : function(removeEl){
41731         Roo.EventManager.removeResizeListener(this.onResize, this);
41732         for(var i = 0, len = this.items.length; i < len; i++){
41733             this.items[i].purgeListeners();
41734         }
41735         if(removeEl === true){
41736             this.el.update("");
41737             this.el.remove();
41738         }
41739     },
41740     
41741     createStrip : function(container)
41742     {
41743         var strip = document.createElement("nav");
41744         strip.className = Roo.bootstrap.version == 4 ?
41745             "navbar-light bg-light" : 
41746             "navbar navbar-default"; //"x-tabs-wrap";
41747         container.appendChild(strip);
41748         return strip;
41749     },
41750     
41751     createStripList : function(strip)
41752     {
41753         // div wrapper for retard IE
41754         // returns the "tr" element.
41755         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41756         //'<div class="x-tabs-strip-wrap">'+
41757           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41758           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41759         return strip.firstChild; //.firstChild.firstChild.firstChild;
41760     },
41761     createBody : function(container)
41762     {
41763         var body = document.createElement("div");
41764         Roo.id(body, "tab-body");
41765         //Roo.fly(body).addClass("x-tabs-body");
41766         Roo.fly(body).addClass("tab-content");
41767         container.appendChild(body);
41768         return body;
41769     },
41770     createItemBody :function(bodyEl, id){
41771         var body = Roo.getDom(id);
41772         if(!body){
41773             body = document.createElement("div");
41774             body.id = id;
41775         }
41776         //Roo.fly(body).addClass("x-tabs-item-body");
41777         Roo.fly(body).addClass("tab-pane");
41778          bodyEl.insertBefore(body, bodyEl.firstChild);
41779         return body;
41780     },
41781     /** @private */
41782     createStripElements :  function(stripEl, text, closable, tpl)
41783     {
41784         var td = document.createElement("li"); // was td..
41785         td.className = 'nav-item';
41786         
41787         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41788         
41789         
41790         stripEl.appendChild(td);
41791         /*if(closable){
41792             td.className = "x-tabs-closable";
41793             if(!this.closeTpl){
41794                 this.closeTpl = new Roo.Template(
41795                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41796                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41797                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41798                 );
41799             }
41800             var el = this.closeTpl.overwrite(td, {"text": text});
41801             var close = el.getElementsByTagName("div")[0];
41802             var inner = el.getElementsByTagName("em")[0];
41803             return {"el": el, "close": close, "inner": inner};
41804         } else {
41805         */
41806         // not sure what this is..
41807 //            if(!this.tabTpl){
41808                 //this.tabTpl = new Roo.Template(
41809                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41810                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41811                 //);
41812 //                this.tabTpl = new Roo.Template(
41813 //                   '<a href="#">' +
41814 //                   '<span unselectable="on"' +
41815 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41816 //                            ' >{text}</span></a>'
41817 //                );
41818 //                
41819 //            }
41820
41821
41822             var template = tpl || this.tabTpl || false;
41823             
41824             if(!template){
41825                 template =  new Roo.Template(
41826                         Roo.bootstrap.version == 4 ? 
41827                             (
41828                                 '<a class="nav-link" href="#" unselectable="on"' +
41829                                      (this.disableTooltips ? '' : ' title="{text}"') +
41830                                      ' >{text}</a>'
41831                             ) : (
41832                                 '<a class="nav-link" href="#">' +
41833                                 '<span unselectable="on"' +
41834                                          (this.disableTooltips ? '' : ' title="{text}"') +
41835                                     ' >{text}</span></a>'
41836                             )
41837                 );
41838             }
41839             
41840             switch (typeof(template)) {
41841                 case 'object' :
41842                     break;
41843                 case 'string' :
41844                     template = new Roo.Template(template);
41845                     break;
41846                 default :
41847                     break;
41848             }
41849             
41850             var el = template.overwrite(td, {"text": text});
41851             
41852             var inner = el.getElementsByTagName("span")[0];
41853             
41854             return {"el": el, "inner": inner};
41855             
41856     }
41857         
41858     
41859 });
41860
41861 /**
41862  * @class Roo.TabPanelItem
41863  * @extends Roo.util.Observable
41864  * Represents an individual item (tab plus body) in a TabPanel.
41865  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41866  * @param {String} id The id of this TabPanelItem
41867  * @param {String} text The text for the tab of this TabPanelItem
41868  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41869  */
41870 Roo.bootstrap.panel.TabItem = function(config){
41871     /**
41872      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41873      * @type Roo.TabPanel
41874      */
41875     this.tabPanel = config.panel;
41876     /**
41877      * The id for this TabPanelItem
41878      * @type String
41879      */
41880     this.id = config.id;
41881     /** @private */
41882     this.disabled = false;
41883     /** @private */
41884     this.text = config.text;
41885     /** @private */
41886     this.loaded = false;
41887     this.closable = config.closable;
41888
41889     /**
41890      * The body element for this TabPanelItem.
41891      * @type Roo.Element
41892      */
41893     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41894     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41895     this.bodyEl.setStyle("display", "block");
41896     this.bodyEl.setStyle("zoom", "1");
41897     //this.hideAction();
41898
41899     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41900     /** @private */
41901     this.el = Roo.get(els.el);
41902     this.inner = Roo.get(els.inner, true);
41903      this.textEl = Roo.bootstrap.version == 4 ?
41904         this.el : Roo.get(this.el.dom.firstChild, true);
41905
41906     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41907     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41908
41909     
41910 //    this.el.on("mousedown", this.onTabMouseDown, this);
41911     this.el.on("click", this.onTabClick, this);
41912     /** @private */
41913     if(config.closable){
41914         var c = Roo.get(els.close, true);
41915         c.dom.title = this.closeText;
41916         c.addClassOnOver("close-over");
41917         c.on("click", this.closeClick, this);
41918      }
41919
41920     this.addEvents({
41921          /**
41922          * @event activate
41923          * Fires when this tab becomes the active tab.
41924          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41925          * @param {Roo.TabPanelItem} this
41926          */
41927         "activate": true,
41928         /**
41929          * @event beforeclose
41930          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41931          * @param {Roo.TabPanelItem} this
41932          * @param {Object} e Set cancel to true on this object to cancel the close.
41933          */
41934         "beforeclose": true,
41935         /**
41936          * @event close
41937          * Fires when this tab is closed.
41938          * @param {Roo.TabPanelItem} this
41939          */
41940          "close": true,
41941         /**
41942          * @event deactivate
41943          * Fires when this tab is no longer the active tab.
41944          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41945          * @param {Roo.TabPanelItem} this
41946          */
41947          "deactivate" : true
41948     });
41949     this.hidden = false;
41950
41951     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41952 };
41953
41954 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41955            {
41956     purgeListeners : function(){
41957        Roo.util.Observable.prototype.purgeListeners.call(this);
41958        this.el.removeAllListeners();
41959     },
41960     /**
41961      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41962      */
41963     show : function(){
41964         this.status_node.addClass("active");
41965         this.showAction();
41966         if(Roo.isOpera){
41967             this.tabPanel.stripWrap.repaint();
41968         }
41969         this.fireEvent("activate", this.tabPanel, this);
41970     },
41971
41972     /**
41973      * Returns true if this tab is the active tab.
41974      * @return {Boolean}
41975      */
41976     isActive : function(){
41977         return this.tabPanel.getActiveTab() == this;
41978     },
41979
41980     /**
41981      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41982      */
41983     hide : function(){
41984         this.status_node.removeClass("active");
41985         this.hideAction();
41986         this.fireEvent("deactivate", this.tabPanel, this);
41987     },
41988
41989     hideAction : function(){
41990         this.bodyEl.hide();
41991         this.bodyEl.setStyle("position", "absolute");
41992         this.bodyEl.setLeft("-20000px");
41993         this.bodyEl.setTop("-20000px");
41994     },
41995
41996     showAction : function(){
41997         this.bodyEl.setStyle("position", "relative");
41998         this.bodyEl.setTop("");
41999         this.bodyEl.setLeft("");
42000         this.bodyEl.show();
42001     },
42002
42003     /**
42004      * Set the tooltip for the tab.
42005      * @param {String} tooltip The tab's tooltip
42006      */
42007     setTooltip : function(text){
42008         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42009             this.textEl.dom.qtip = text;
42010             this.textEl.dom.removeAttribute('title');
42011         }else{
42012             this.textEl.dom.title = text;
42013         }
42014     },
42015
42016     onTabClick : function(e){
42017         e.preventDefault();
42018         this.tabPanel.activate(this.id);
42019     },
42020
42021     onTabMouseDown : function(e){
42022         e.preventDefault();
42023         this.tabPanel.activate(this.id);
42024     },
42025 /*
42026     getWidth : function(){
42027         return this.inner.getWidth();
42028     },
42029
42030     setWidth : function(width){
42031         var iwidth = width - this.linode.getPadding("lr");
42032         this.inner.setWidth(iwidth);
42033         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42034         this.linode.setWidth(width);
42035     },
42036 */
42037     /**
42038      * Show or hide the tab
42039      * @param {Boolean} hidden True to hide or false to show.
42040      */
42041     setHidden : function(hidden){
42042         this.hidden = hidden;
42043         this.linode.setStyle("display", hidden ? "none" : "");
42044     },
42045
42046     /**
42047      * Returns true if this tab is "hidden"
42048      * @return {Boolean}
42049      */
42050     isHidden : function(){
42051         return this.hidden;
42052     },
42053
42054     /**
42055      * Returns the text for this tab
42056      * @return {String}
42057      */
42058     getText : function(){
42059         return this.text;
42060     },
42061     /*
42062     autoSize : function(){
42063         //this.el.beginMeasure();
42064         this.textEl.setWidth(1);
42065         /*
42066          *  #2804 [new] Tabs in Roojs
42067          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42068          */
42069         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42070         //this.el.endMeasure();
42071     //},
42072
42073     /**
42074      * Sets the text for the tab (Note: this also sets the tooltip text)
42075      * @param {String} text The tab's text and tooltip
42076      */
42077     setText : function(text){
42078         this.text = text;
42079         this.textEl.update(text);
42080         this.setTooltip(text);
42081         //if(!this.tabPanel.resizeTabs){
42082         //    this.autoSize();
42083         //}
42084     },
42085     /**
42086      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42087      */
42088     activate : function(){
42089         this.tabPanel.activate(this.id);
42090     },
42091
42092     /**
42093      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42094      */
42095     disable : function(){
42096         if(this.tabPanel.active != this){
42097             this.disabled = true;
42098             this.status_node.addClass("disabled");
42099         }
42100     },
42101
42102     /**
42103      * Enables this TabPanelItem if it was previously disabled.
42104      */
42105     enable : function(){
42106         this.disabled = false;
42107         this.status_node.removeClass("disabled");
42108     },
42109
42110     /**
42111      * Sets the content for this TabPanelItem.
42112      * @param {String} content The content
42113      * @param {Boolean} loadScripts true to look for and load scripts
42114      */
42115     setContent : function(content, loadScripts){
42116         this.bodyEl.update(content, loadScripts);
42117     },
42118
42119     /**
42120      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42121      * @return {Roo.UpdateManager} The UpdateManager
42122      */
42123     getUpdateManager : function(){
42124         return this.bodyEl.getUpdateManager();
42125     },
42126
42127     /**
42128      * Set a URL to be used to load the content for this TabPanelItem.
42129      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42130      * @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)
42131      * @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)
42132      * @return {Roo.UpdateManager} The UpdateManager
42133      */
42134     setUrl : function(url, params, loadOnce){
42135         if(this.refreshDelegate){
42136             this.un('activate', this.refreshDelegate);
42137         }
42138         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42139         this.on("activate", this.refreshDelegate);
42140         return this.bodyEl.getUpdateManager();
42141     },
42142
42143     /** @private */
42144     _handleRefresh : function(url, params, loadOnce){
42145         if(!loadOnce || !this.loaded){
42146             var updater = this.bodyEl.getUpdateManager();
42147             updater.update(url, params, this._setLoaded.createDelegate(this));
42148         }
42149     },
42150
42151     /**
42152      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42153      *   Will fail silently if the setUrl method has not been called.
42154      *   This does not activate the panel, just updates its content.
42155      */
42156     refresh : function(){
42157         if(this.refreshDelegate){
42158            this.loaded = false;
42159            this.refreshDelegate();
42160         }
42161     },
42162
42163     /** @private */
42164     _setLoaded : function(){
42165         this.loaded = true;
42166     },
42167
42168     /** @private */
42169     closeClick : function(e){
42170         var o = {};
42171         e.stopEvent();
42172         this.fireEvent("beforeclose", this, o);
42173         if(o.cancel !== true){
42174             this.tabPanel.removeTab(this.id);
42175         }
42176     },
42177     /**
42178      * The text displayed in the tooltip for the close icon.
42179      * @type String
42180      */
42181     closeText : "Close this tab"
42182 });
42183 /**
42184 *    This script refer to:
42185 *    Title: International Telephone Input
42186 *    Author: Jack O'Connor
42187 *    Code version:  v12.1.12
42188 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42189 **/
42190
42191 Roo.bootstrap.PhoneInputData = function() {
42192     var d = [
42193       [
42194         "Afghanistan (‫افغانستان‬‎)",
42195         "af",
42196         "93"
42197       ],
42198       [
42199         "Albania (Shqipëri)",
42200         "al",
42201         "355"
42202       ],
42203       [
42204         "Algeria (‫الجزائر‬‎)",
42205         "dz",
42206         "213"
42207       ],
42208       [
42209         "American Samoa",
42210         "as",
42211         "1684"
42212       ],
42213       [
42214         "Andorra",
42215         "ad",
42216         "376"
42217       ],
42218       [
42219         "Angola",
42220         "ao",
42221         "244"
42222       ],
42223       [
42224         "Anguilla",
42225         "ai",
42226         "1264"
42227       ],
42228       [
42229         "Antigua and Barbuda",
42230         "ag",
42231         "1268"
42232       ],
42233       [
42234         "Argentina",
42235         "ar",
42236         "54"
42237       ],
42238       [
42239         "Armenia (Հայաստան)",
42240         "am",
42241         "374"
42242       ],
42243       [
42244         "Aruba",
42245         "aw",
42246         "297"
42247       ],
42248       [
42249         "Australia",
42250         "au",
42251         "61",
42252         0
42253       ],
42254       [
42255         "Austria (Österreich)",
42256         "at",
42257         "43"
42258       ],
42259       [
42260         "Azerbaijan (Azərbaycan)",
42261         "az",
42262         "994"
42263       ],
42264       [
42265         "Bahamas",
42266         "bs",
42267         "1242"
42268       ],
42269       [
42270         "Bahrain (‫البحرين‬‎)",
42271         "bh",
42272         "973"
42273       ],
42274       [
42275         "Bangladesh (বাংলাদেশ)",
42276         "bd",
42277         "880"
42278       ],
42279       [
42280         "Barbados",
42281         "bb",
42282         "1246"
42283       ],
42284       [
42285         "Belarus (Беларусь)",
42286         "by",
42287         "375"
42288       ],
42289       [
42290         "Belgium (België)",
42291         "be",
42292         "32"
42293       ],
42294       [
42295         "Belize",
42296         "bz",
42297         "501"
42298       ],
42299       [
42300         "Benin (Bénin)",
42301         "bj",
42302         "229"
42303       ],
42304       [
42305         "Bermuda",
42306         "bm",
42307         "1441"
42308       ],
42309       [
42310         "Bhutan (འབྲུག)",
42311         "bt",
42312         "975"
42313       ],
42314       [
42315         "Bolivia",
42316         "bo",
42317         "591"
42318       ],
42319       [
42320         "Bosnia and Herzegovina (Босна и Херцеговина)",
42321         "ba",
42322         "387"
42323       ],
42324       [
42325         "Botswana",
42326         "bw",
42327         "267"
42328       ],
42329       [
42330         "Brazil (Brasil)",
42331         "br",
42332         "55"
42333       ],
42334       [
42335         "British Indian Ocean Territory",
42336         "io",
42337         "246"
42338       ],
42339       [
42340         "British Virgin Islands",
42341         "vg",
42342         "1284"
42343       ],
42344       [
42345         "Brunei",
42346         "bn",
42347         "673"
42348       ],
42349       [
42350         "Bulgaria (България)",
42351         "bg",
42352         "359"
42353       ],
42354       [
42355         "Burkina Faso",
42356         "bf",
42357         "226"
42358       ],
42359       [
42360         "Burundi (Uburundi)",
42361         "bi",
42362         "257"
42363       ],
42364       [
42365         "Cambodia (កម្ពុជា)",
42366         "kh",
42367         "855"
42368       ],
42369       [
42370         "Cameroon (Cameroun)",
42371         "cm",
42372         "237"
42373       ],
42374       [
42375         "Canada",
42376         "ca",
42377         "1",
42378         1,
42379         ["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"]
42380       ],
42381       [
42382         "Cape Verde (Kabu Verdi)",
42383         "cv",
42384         "238"
42385       ],
42386       [
42387         "Caribbean Netherlands",
42388         "bq",
42389         "599",
42390         1
42391       ],
42392       [
42393         "Cayman Islands",
42394         "ky",
42395         "1345"
42396       ],
42397       [
42398         "Central African Republic (République centrafricaine)",
42399         "cf",
42400         "236"
42401       ],
42402       [
42403         "Chad (Tchad)",
42404         "td",
42405         "235"
42406       ],
42407       [
42408         "Chile",
42409         "cl",
42410         "56"
42411       ],
42412       [
42413         "China (中国)",
42414         "cn",
42415         "86"
42416       ],
42417       [
42418         "Christmas Island",
42419         "cx",
42420         "61",
42421         2
42422       ],
42423       [
42424         "Cocos (Keeling) Islands",
42425         "cc",
42426         "61",
42427         1
42428       ],
42429       [
42430         "Colombia",
42431         "co",
42432         "57"
42433       ],
42434       [
42435         "Comoros (‫جزر القمر‬‎)",
42436         "km",
42437         "269"
42438       ],
42439       [
42440         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42441         "cd",
42442         "243"
42443       ],
42444       [
42445         "Congo (Republic) (Congo-Brazzaville)",
42446         "cg",
42447         "242"
42448       ],
42449       [
42450         "Cook Islands",
42451         "ck",
42452         "682"
42453       ],
42454       [
42455         "Costa Rica",
42456         "cr",
42457         "506"
42458       ],
42459       [
42460         "Côte d’Ivoire",
42461         "ci",
42462         "225"
42463       ],
42464       [
42465         "Croatia (Hrvatska)",
42466         "hr",
42467         "385"
42468       ],
42469       [
42470         "Cuba",
42471         "cu",
42472         "53"
42473       ],
42474       [
42475         "Curaçao",
42476         "cw",
42477         "599",
42478         0
42479       ],
42480       [
42481         "Cyprus (Κύπρος)",
42482         "cy",
42483         "357"
42484       ],
42485       [
42486         "Czech Republic (Česká republika)",
42487         "cz",
42488         "420"
42489       ],
42490       [
42491         "Denmark (Danmark)",
42492         "dk",
42493         "45"
42494       ],
42495       [
42496         "Djibouti",
42497         "dj",
42498         "253"
42499       ],
42500       [
42501         "Dominica",
42502         "dm",
42503         "1767"
42504       ],
42505       [
42506         "Dominican Republic (República Dominicana)",
42507         "do",
42508         "1",
42509         2,
42510         ["809", "829", "849"]
42511       ],
42512       [
42513         "Ecuador",
42514         "ec",
42515         "593"
42516       ],
42517       [
42518         "Egypt (‫مصر‬‎)",
42519         "eg",
42520         "20"
42521       ],
42522       [
42523         "El Salvador",
42524         "sv",
42525         "503"
42526       ],
42527       [
42528         "Equatorial Guinea (Guinea Ecuatorial)",
42529         "gq",
42530         "240"
42531       ],
42532       [
42533         "Eritrea",
42534         "er",
42535         "291"
42536       ],
42537       [
42538         "Estonia (Eesti)",
42539         "ee",
42540         "372"
42541       ],
42542       [
42543         "Ethiopia",
42544         "et",
42545         "251"
42546       ],
42547       [
42548         "Falkland Islands (Islas Malvinas)",
42549         "fk",
42550         "500"
42551       ],
42552       [
42553         "Faroe Islands (Føroyar)",
42554         "fo",
42555         "298"
42556       ],
42557       [
42558         "Fiji",
42559         "fj",
42560         "679"
42561       ],
42562       [
42563         "Finland (Suomi)",
42564         "fi",
42565         "358",
42566         0
42567       ],
42568       [
42569         "France",
42570         "fr",
42571         "33"
42572       ],
42573       [
42574         "French Guiana (Guyane française)",
42575         "gf",
42576         "594"
42577       ],
42578       [
42579         "French Polynesia (Polynésie française)",
42580         "pf",
42581         "689"
42582       ],
42583       [
42584         "Gabon",
42585         "ga",
42586         "241"
42587       ],
42588       [
42589         "Gambia",
42590         "gm",
42591         "220"
42592       ],
42593       [
42594         "Georgia (საქართველო)",
42595         "ge",
42596         "995"
42597       ],
42598       [
42599         "Germany (Deutschland)",
42600         "de",
42601         "49"
42602       ],
42603       [
42604         "Ghana (Gaana)",
42605         "gh",
42606         "233"
42607       ],
42608       [
42609         "Gibraltar",
42610         "gi",
42611         "350"
42612       ],
42613       [
42614         "Greece (Ελλάδα)",
42615         "gr",
42616         "30"
42617       ],
42618       [
42619         "Greenland (Kalaallit Nunaat)",
42620         "gl",
42621         "299"
42622       ],
42623       [
42624         "Grenada",
42625         "gd",
42626         "1473"
42627       ],
42628       [
42629         "Guadeloupe",
42630         "gp",
42631         "590",
42632         0
42633       ],
42634       [
42635         "Guam",
42636         "gu",
42637         "1671"
42638       ],
42639       [
42640         "Guatemala",
42641         "gt",
42642         "502"
42643       ],
42644       [
42645         "Guernsey",
42646         "gg",
42647         "44",
42648         1
42649       ],
42650       [
42651         "Guinea (Guinée)",
42652         "gn",
42653         "224"
42654       ],
42655       [
42656         "Guinea-Bissau (Guiné Bissau)",
42657         "gw",
42658         "245"
42659       ],
42660       [
42661         "Guyana",
42662         "gy",
42663         "592"
42664       ],
42665       [
42666         "Haiti",
42667         "ht",
42668         "509"
42669       ],
42670       [
42671         "Honduras",
42672         "hn",
42673         "504"
42674       ],
42675       [
42676         "Hong Kong (香港)",
42677         "hk",
42678         "852"
42679       ],
42680       [
42681         "Hungary (Magyarország)",
42682         "hu",
42683         "36"
42684       ],
42685       [
42686         "Iceland (Ísland)",
42687         "is",
42688         "354"
42689       ],
42690       [
42691         "India (भारत)",
42692         "in",
42693         "91"
42694       ],
42695       [
42696         "Indonesia",
42697         "id",
42698         "62"
42699       ],
42700       [
42701         "Iran (‫ایران‬‎)",
42702         "ir",
42703         "98"
42704       ],
42705       [
42706         "Iraq (‫العراق‬‎)",
42707         "iq",
42708         "964"
42709       ],
42710       [
42711         "Ireland",
42712         "ie",
42713         "353"
42714       ],
42715       [
42716         "Isle of Man",
42717         "im",
42718         "44",
42719         2
42720       ],
42721       [
42722         "Israel (‫ישראל‬‎)",
42723         "il",
42724         "972"
42725       ],
42726       [
42727         "Italy (Italia)",
42728         "it",
42729         "39",
42730         0
42731       ],
42732       [
42733         "Jamaica",
42734         "jm",
42735         "1876"
42736       ],
42737       [
42738         "Japan (日本)",
42739         "jp",
42740         "81"
42741       ],
42742       [
42743         "Jersey",
42744         "je",
42745         "44",
42746         3
42747       ],
42748       [
42749         "Jordan (‫الأردن‬‎)",
42750         "jo",
42751         "962"
42752       ],
42753       [
42754         "Kazakhstan (Казахстан)",
42755         "kz",
42756         "7",
42757         1
42758       ],
42759       [
42760         "Kenya",
42761         "ke",
42762         "254"
42763       ],
42764       [
42765         "Kiribati",
42766         "ki",
42767         "686"
42768       ],
42769       [
42770         "Kosovo",
42771         "xk",
42772         "383"
42773       ],
42774       [
42775         "Kuwait (‫الكويت‬‎)",
42776         "kw",
42777         "965"
42778       ],
42779       [
42780         "Kyrgyzstan (Кыргызстан)",
42781         "kg",
42782         "996"
42783       ],
42784       [
42785         "Laos (ລາວ)",
42786         "la",
42787         "856"
42788       ],
42789       [
42790         "Latvia (Latvija)",
42791         "lv",
42792         "371"
42793       ],
42794       [
42795         "Lebanon (‫لبنان‬‎)",
42796         "lb",
42797         "961"
42798       ],
42799       [
42800         "Lesotho",
42801         "ls",
42802         "266"
42803       ],
42804       [
42805         "Liberia",
42806         "lr",
42807         "231"
42808       ],
42809       [
42810         "Libya (‫ليبيا‬‎)",
42811         "ly",
42812         "218"
42813       ],
42814       [
42815         "Liechtenstein",
42816         "li",
42817         "423"
42818       ],
42819       [
42820         "Lithuania (Lietuva)",
42821         "lt",
42822         "370"
42823       ],
42824       [
42825         "Luxembourg",
42826         "lu",
42827         "352"
42828       ],
42829       [
42830         "Macau (澳門)",
42831         "mo",
42832         "853"
42833       ],
42834       [
42835         "Macedonia (FYROM) (Македонија)",
42836         "mk",
42837         "389"
42838       ],
42839       [
42840         "Madagascar (Madagasikara)",
42841         "mg",
42842         "261"
42843       ],
42844       [
42845         "Malawi",
42846         "mw",
42847         "265"
42848       ],
42849       [
42850         "Malaysia",
42851         "my",
42852         "60"
42853       ],
42854       [
42855         "Maldives",
42856         "mv",
42857         "960"
42858       ],
42859       [
42860         "Mali",
42861         "ml",
42862         "223"
42863       ],
42864       [
42865         "Malta",
42866         "mt",
42867         "356"
42868       ],
42869       [
42870         "Marshall Islands",
42871         "mh",
42872         "692"
42873       ],
42874       [
42875         "Martinique",
42876         "mq",
42877         "596"
42878       ],
42879       [
42880         "Mauritania (‫موريتانيا‬‎)",
42881         "mr",
42882         "222"
42883       ],
42884       [
42885         "Mauritius (Moris)",
42886         "mu",
42887         "230"
42888       ],
42889       [
42890         "Mayotte",
42891         "yt",
42892         "262",
42893         1
42894       ],
42895       [
42896         "Mexico (México)",
42897         "mx",
42898         "52"
42899       ],
42900       [
42901         "Micronesia",
42902         "fm",
42903         "691"
42904       ],
42905       [
42906         "Moldova (Republica Moldova)",
42907         "md",
42908         "373"
42909       ],
42910       [
42911         "Monaco",
42912         "mc",
42913         "377"
42914       ],
42915       [
42916         "Mongolia (Монгол)",
42917         "mn",
42918         "976"
42919       ],
42920       [
42921         "Montenegro (Crna Gora)",
42922         "me",
42923         "382"
42924       ],
42925       [
42926         "Montserrat",
42927         "ms",
42928         "1664"
42929       ],
42930       [
42931         "Morocco (‫المغرب‬‎)",
42932         "ma",
42933         "212",
42934         0
42935       ],
42936       [
42937         "Mozambique (Moçambique)",
42938         "mz",
42939         "258"
42940       ],
42941       [
42942         "Myanmar (Burma) (မြန်မာ)",
42943         "mm",
42944         "95"
42945       ],
42946       [
42947         "Namibia (Namibië)",
42948         "na",
42949         "264"
42950       ],
42951       [
42952         "Nauru",
42953         "nr",
42954         "674"
42955       ],
42956       [
42957         "Nepal (नेपाल)",
42958         "np",
42959         "977"
42960       ],
42961       [
42962         "Netherlands (Nederland)",
42963         "nl",
42964         "31"
42965       ],
42966       [
42967         "New Caledonia (Nouvelle-Calédonie)",
42968         "nc",
42969         "687"
42970       ],
42971       [
42972         "New Zealand",
42973         "nz",
42974         "64"
42975       ],
42976       [
42977         "Nicaragua",
42978         "ni",
42979         "505"
42980       ],
42981       [
42982         "Niger (Nijar)",
42983         "ne",
42984         "227"
42985       ],
42986       [
42987         "Nigeria",
42988         "ng",
42989         "234"
42990       ],
42991       [
42992         "Niue",
42993         "nu",
42994         "683"
42995       ],
42996       [
42997         "Norfolk Island",
42998         "nf",
42999         "672"
43000       ],
43001       [
43002         "North Korea (조선 민주주의 인민 공화국)",
43003         "kp",
43004         "850"
43005       ],
43006       [
43007         "Northern Mariana Islands",
43008         "mp",
43009         "1670"
43010       ],
43011       [
43012         "Norway (Norge)",
43013         "no",
43014         "47",
43015         0
43016       ],
43017       [
43018         "Oman (‫عُمان‬‎)",
43019         "om",
43020         "968"
43021       ],
43022       [
43023         "Pakistan (‫پاکستان‬‎)",
43024         "pk",
43025         "92"
43026       ],
43027       [
43028         "Palau",
43029         "pw",
43030         "680"
43031       ],
43032       [
43033         "Palestine (‫فلسطين‬‎)",
43034         "ps",
43035         "970"
43036       ],
43037       [
43038         "Panama (Panamá)",
43039         "pa",
43040         "507"
43041       ],
43042       [
43043         "Papua New Guinea",
43044         "pg",
43045         "675"
43046       ],
43047       [
43048         "Paraguay",
43049         "py",
43050         "595"
43051       ],
43052       [
43053         "Peru (Perú)",
43054         "pe",
43055         "51"
43056       ],
43057       [
43058         "Philippines",
43059         "ph",
43060         "63"
43061       ],
43062       [
43063         "Poland (Polska)",
43064         "pl",
43065         "48"
43066       ],
43067       [
43068         "Portugal",
43069         "pt",
43070         "351"
43071       ],
43072       [
43073         "Puerto Rico",
43074         "pr",
43075         "1",
43076         3,
43077         ["787", "939"]
43078       ],
43079       [
43080         "Qatar (‫قطر‬‎)",
43081         "qa",
43082         "974"
43083       ],
43084       [
43085         "Réunion (La Réunion)",
43086         "re",
43087         "262",
43088         0
43089       ],
43090       [
43091         "Romania (România)",
43092         "ro",
43093         "40"
43094       ],
43095       [
43096         "Russia (Россия)",
43097         "ru",
43098         "7",
43099         0
43100       ],
43101       [
43102         "Rwanda",
43103         "rw",
43104         "250"
43105       ],
43106       [
43107         "Saint Barthélemy",
43108         "bl",
43109         "590",
43110         1
43111       ],
43112       [
43113         "Saint Helena",
43114         "sh",
43115         "290"
43116       ],
43117       [
43118         "Saint Kitts and Nevis",
43119         "kn",
43120         "1869"
43121       ],
43122       [
43123         "Saint Lucia",
43124         "lc",
43125         "1758"
43126       ],
43127       [
43128         "Saint Martin (Saint-Martin (partie française))",
43129         "mf",
43130         "590",
43131         2
43132       ],
43133       [
43134         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43135         "pm",
43136         "508"
43137       ],
43138       [
43139         "Saint Vincent and the Grenadines",
43140         "vc",
43141         "1784"
43142       ],
43143       [
43144         "Samoa",
43145         "ws",
43146         "685"
43147       ],
43148       [
43149         "San Marino",
43150         "sm",
43151         "378"
43152       ],
43153       [
43154         "São Tomé and Príncipe (São Tomé e Príncipe)",
43155         "st",
43156         "239"
43157       ],
43158       [
43159         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43160         "sa",
43161         "966"
43162       ],
43163       [
43164         "Senegal (Sénégal)",
43165         "sn",
43166         "221"
43167       ],
43168       [
43169         "Serbia (Србија)",
43170         "rs",
43171         "381"
43172       ],
43173       [
43174         "Seychelles",
43175         "sc",
43176         "248"
43177       ],
43178       [
43179         "Sierra Leone",
43180         "sl",
43181         "232"
43182       ],
43183       [
43184         "Singapore",
43185         "sg",
43186         "65"
43187       ],
43188       [
43189         "Sint Maarten",
43190         "sx",
43191         "1721"
43192       ],
43193       [
43194         "Slovakia (Slovensko)",
43195         "sk",
43196         "421"
43197       ],
43198       [
43199         "Slovenia (Slovenija)",
43200         "si",
43201         "386"
43202       ],
43203       [
43204         "Solomon Islands",
43205         "sb",
43206         "677"
43207       ],
43208       [
43209         "Somalia (Soomaaliya)",
43210         "so",
43211         "252"
43212       ],
43213       [
43214         "South Africa",
43215         "za",
43216         "27"
43217       ],
43218       [
43219         "South Korea (대한민국)",
43220         "kr",
43221         "82"
43222       ],
43223       [
43224         "South Sudan (‫جنوب السودان‬‎)",
43225         "ss",
43226         "211"
43227       ],
43228       [
43229         "Spain (España)",
43230         "es",
43231         "34"
43232       ],
43233       [
43234         "Sri Lanka (ශ්‍රී ලංකාව)",
43235         "lk",
43236         "94"
43237       ],
43238       [
43239         "Sudan (‫السودان‬‎)",
43240         "sd",
43241         "249"
43242       ],
43243       [
43244         "Suriname",
43245         "sr",
43246         "597"
43247       ],
43248       [
43249         "Svalbard and Jan Mayen",
43250         "sj",
43251         "47",
43252         1
43253       ],
43254       [
43255         "Swaziland",
43256         "sz",
43257         "268"
43258       ],
43259       [
43260         "Sweden (Sverige)",
43261         "se",
43262         "46"
43263       ],
43264       [
43265         "Switzerland (Schweiz)",
43266         "ch",
43267         "41"
43268       ],
43269       [
43270         "Syria (‫سوريا‬‎)",
43271         "sy",
43272         "963"
43273       ],
43274       [
43275         "Taiwan (台灣)",
43276         "tw",
43277         "886"
43278       ],
43279       [
43280         "Tajikistan",
43281         "tj",
43282         "992"
43283       ],
43284       [
43285         "Tanzania",
43286         "tz",
43287         "255"
43288       ],
43289       [
43290         "Thailand (ไทย)",
43291         "th",
43292         "66"
43293       ],
43294       [
43295         "Timor-Leste",
43296         "tl",
43297         "670"
43298       ],
43299       [
43300         "Togo",
43301         "tg",
43302         "228"
43303       ],
43304       [
43305         "Tokelau",
43306         "tk",
43307         "690"
43308       ],
43309       [
43310         "Tonga",
43311         "to",
43312         "676"
43313       ],
43314       [
43315         "Trinidad and Tobago",
43316         "tt",
43317         "1868"
43318       ],
43319       [
43320         "Tunisia (‫تونس‬‎)",
43321         "tn",
43322         "216"
43323       ],
43324       [
43325         "Turkey (Türkiye)",
43326         "tr",
43327         "90"
43328       ],
43329       [
43330         "Turkmenistan",
43331         "tm",
43332         "993"
43333       ],
43334       [
43335         "Turks and Caicos Islands",
43336         "tc",
43337         "1649"
43338       ],
43339       [
43340         "Tuvalu",
43341         "tv",
43342         "688"
43343       ],
43344       [
43345         "U.S. Virgin Islands",
43346         "vi",
43347         "1340"
43348       ],
43349       [
43350         "Uganda",
43351         "ug",
43352         "256"
43353       ],
43354       [
43355         "Ukraine (Україна)",
43356         "ua",
43357         "380"
43358       ],
43359       [
43360         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43361         "ae",
43362         "971"
43363       ],
43364       [
43365         "United Kingdom",
43366         "gb",
43367         "44",
43368         0
43369       ],
43370       [
43371         "United States",
43372         "us",
43373         "1",
43374         0
43375       ],
43376       [
43377         "Uruguay",
43378         "uy",
43379         "598"
43380       ],
43381       [
43382         "Uzbekistan (Oʻzbekiston)",
43383         "uz",
43384         "998"
43385       ],
43386       [
43387         "Vanuatu",
43388         "vu",
43389         "678"
43390       ],
43391       [
43392         "Vatican City (Città del Vaticano)",
43393         "va",
43394         "39",
43395         1
43396       ],
43397       [
43398         "Venezuela",
43399         "ve",
43400         "58"
43401       ],
43402       [
43403         "Vietnam (Việt Nam)",
43404         "vn",
43405         "84"
43406       ],
43407       [
43408         "Wallis and Futuna (Wallis-et-Futuna)",
43409         "wf",
43410         "681"
43411       ],
43412       [
43413         "Western Sahara (‫الصحراء الغربية‬‎)",
43414         "eh",
43415         "212",
43416         1
43417       ],
43418       [
43419         "Yemen (‫اليمن‬‎)",
43420         "ye",
43421         "967"
43422       ],
43423       [
43424         "Zambia",
43425         "zm",
43426         "260"
43427       ],
43428       [
43429         "Zimbabwe",
43430         "zw",
43431         "263"
43432       ],
43433       [
43434         "Åland Islands",
43435         "ax",
43436         "358",
43437         1
43438       ]
43439   ];
43440   
43441   return d;
43442 }/**
43443 *    This script refer to:
43444 *    Title: International Telephone Input
43445 *    Author: Jack O'Connor
43446 *    Code version:  v12.1.12
43447 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43448 **/
43449
43450 /**
43451  * @class Roo.bootstrap.PhoneInput
43452  * @extends Roo.bootstrap.TriggerField
43453  * An input with International dial-code selection
43454  
43455  * @cfg {String} defaultDialCode default '+852'
43456  * @cfg {Array} preferedCountries default []
43457   
43458  * @constructor
43459  * Create a new PhoneInput.
43460  * @param {Object} config Configuration options
43461  */
43462
43463 Roo.bootstrap.PhoneInput = function(config) {
43464     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43465 };
43466
43467 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43468         
43469         listWidth: undefined,
43470         
43471         selectedClass: 'active',
43472         
43473         invalidClass : "has-warning",
43474         
43475         validClass: 'has-success',
43476         
43477         allowed: '0123456789',
43478         
43479         max_length: 15,
43480         
43481         /**
43482          * @cfg {String} defaultDialCode The default dial code when initializing the input
43483          */
43484         defaultDialCode: '+852',
43485         
43486         /**
43487          * @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
43488          */
43489         preferedCountries: false,
43490         
43491         getAutoCreate : function()
43492         {
43493             var data = Roo.bootstrap.PhoneInputData();
43494             var align = this.labelAlign || this.parentLabelAlign();
43495             var id = Roo.id();
43496             
43497             this.allCountries = [];
43498             this.dialCodeMapping = [];
43499             
43500             for (var i = 0; i < data.length; i++) {
43501               var c = data[i];
43502               this.allCountries[i] = {
43503                 name: c[0],
43504                 iso2: c[1],
43505                 dialCode: c[2],
43506                 priority: c[3] || 0,
43507                 areaCodes: c[4] || null
43508               };
43509               this.dialCodeMapping[c[2]] = {
43510                   name: c[0],
43511                   iso2: c[1],
43512                   priority: c[3] || 0,
43513                   areaCodes: c[4] || null
43514               };
43515             }
43516             
43517             var cfg = {
43518                 cls: 'form-group',
43519                 cn: []
43520             };
43521             
43522             var input =  {
43523                 tag: 'input',
43524                 id : id,
43525                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43526                 maxlength: this.max_length,
43527                 cls : 'form-control tel-input',
43528                 autocomplete: 'new-password'
43529             };
43530             
43531             var hiddenInput = {
43532                 tag: 'input',
43533                 type: 'hidden',
43534                 cls: 'hidden-tel-input'
43535             };
43536             
43537             if (this.name) {
43538                 hiddenInput.name = this.name;
43539             }
43540             
43541             if (this.disabled) {
43542                 input.disabled = true;
43543             }
43544             
43545             var flag_container = {
43546                 tag: 'div',
43547                 cls: 'flag-box',
43548                 cn: [
43549                     {
43550                         tag: 'div',
43551                         cls: 'flag'
43552                     },
43553                     {
43554                         tag: 'div',
43555                         cls: 'caret'
43556                     }
43557                 ]
43558             };
43559             
43560             var box = {
43561                 tag: 'div',
43562                 cls: this.hasFeedback ? 'has-feedback' : '',
43563                 cn: [
43564                     hiddenInput,
43565                     input,
43566                     {
43567                         tag: 'input',
43568                         cls: 'dial-code-holder',
43569                         disabled: true
43570                     }
43571                 ]
43572             };
43573             
43574             var container = {
43575                 cls: 'roo-select2-container input-group',
43576                 cn: [
43577                     flag_container,
43578                     box
43579                 ]
43580             };
43581             
43582             if (this.fieldLabel.length) {
43583                 var indicator = {
43584                     tag: 'i',
43585                     tooltip: 'This field is required'
43586                 };
43587                 
43588                 var label = {
43589                     tag: 'label',
43590                     'for':  id,
43591                     cls: 'control-label',
43592                     cn: []
43593                 };
43594                 
43595                 var label_text = {
43596                     tag: 'span',
43597                     html: this.fieldLabel
43598                 };
43599                 
43600                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43601                 label.cn = [
43602                     indicator,
43603                     label_text
43604                 ];
43605                 
43606                 if(this.indicatorpos == 'right') {
43607                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43608                     label.cn = [
43609                         label_text,
43610                         indicator
43611                     ];
43612                 }
43613                 
43614                 if(align == 'left') {
43615                     container = {
43616                         tag: 'div',
43617                         cn: [
43618                             container
43619                         ]
43620                     };
43621                     
43622                     if(this.labelWidth > 12){
43623                         label.style = "width: " + this.labelWidth + 'px';
43624                     }
43625                     if(this.labelWidth < 13 && this.labelmd == 0){
43626                         this.labelmd = this.labelWidth;
43627                     }
43628                     if(this.labellg > 0){
43629                         label.cls += ' col-lg-' + this.labellg;
43630                         input.cls += ' col-lg-' + (12 - this.labellg);
43631                     }
43632                     if(this.labelmd > 0){
43633                         label.cls += ' col-md-' + this.labelmd;
43634                         container.cls += ' col-md-' + (12 - this.labelmd);
43635                     }
43636                     if(this.labelsm > 0){
43637                         label.cls += ' col-sm-' + this.labelsm;
43638                         container.cls += ' col-sm-' + (12 - this.labelsm);
43639                     }
43640                     if(this.labelxs > 0){
43641                         label.cls += ' col-xs-' + this.labelxs;
43642                         container.cls += ' col-xs-' + (12 - this.labelxs);
43643                     }
43644                 }
43645             }
43646             
43647             cfg.cn = [
43648                 label,
43649                 container
43650             ];
43651             
43652             var settings = this;
43653             
43654             ['xs','sm','md','lg'].map(function(size){
43655                 if (settings[size]) {
43656                     cfg.cls += ' col-' + size + '-' + settings[size];
43657                 }
43658             });
43659             
43660             this.store = new Roo.data.Store({
43661                 proxy : new Roo.data.MemoryProxy({}),
43662                 reader : new Roo.data.JsonReader({
43663                     fields : [
43664                         {
43665                             'name' : 'name',
43666                             'type' : 'string'
43667                         },
43668                         {
43669                             'name' : 'iso2',
43670                             'type' : 'string'
43671                         },
43672                         {
43673                             'name' : 'dialCode',
43674                             'type' : 'string'
43675                         },
43676                         {
43677                             'name' : 'priority',
43678                             'type' : 'string'
43679                         },
43680                         {
43681                             'name' : 'areaCodes',
43682                             'type' : 'string'
43683                         }
43684                     ]
43685                 })
43686             });
43687             
43688             if(!this.preferedCountries) {
43689                 this.preferedCountries = [
43690                     'hk',
43691                     'gb',
43692                     'us'
43693                 ];
43694             }
43695             
43696             var p = this.preferedCountries.reverse();
43697             
43698             if(p) {
43699                 for (var i = 0; i < p.length; i++) {
43700                     for (var j = 0; j < this.allCountries.length; j++) {
43701                         if(this.allCountries[j].iso2 == p[i]) {
43702                             var t = this.allCountries[j];
43703                             this.allCountries.splice(j,1);
43704                             this.allCountries.unshift(t);
43705                         }
43706                     } 
43707                 }
43708             }
43709             
43710             this.store.proxy.data = {
43711                 success: true,
43712                 data: this.allCountries
43713             };
43714             
43715             return cfg;
43716         },
43717         
43718         initEvents : function()
43719         {
43720             this.createList();
43721             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43722             
43723             this.indicator = this.indicatorEl();
43724             this.flag = this.flagEl();
43725             this.dialCodeHolder = this.dialCodeHolderEl();
43726             
43727             this.trigger = this.el.select('div.flag-box',true).first();
43728             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43729             
43730             var _this = this;
43731             
43732             (function(){
43733                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43734                 _this.list.setWidth(lw);
43735             }).defer(100);
43736             
43737             this.list.on('mouseover', this.onViewOver, this);
43738             this.list.on('mousemove', this.onViewMove, this);
43739             this.inputEl().on("keyup", this.onKeyUp, this);
43740             this.inputEl().on("keypress", this.onKeyPress, this);
43741             
43742             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43743
43744             this.view = new Roo.View(this.list, this.tpl, {
43745                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43746             });
43747             
43748             this.view.on('click', this.onViewClick, this);
43749             this.setValue(this.defaultDialCode);
43750         },
43751         
43752         onTriggerClick : function(e)
43753         {
43754             Roo.log('trigger click');
43755             if(this.disabled){
43756                 return;
43757             }
43758             
43759             if(this.isExpanded()){
43760                 this.collapse();
43761                 this.hasFocus = false;
43762             }else {
43763                 this.store.load({});
43764                 this.hasFocus = true;
43765                 this.expand();
43766             }
43767         },
43768         
43769         isExpanded : function()
43770         {
43771             return this.list.isVisible();
43772         },
43773         
43774         collapse : function()
43775         {
43776             if(!this.isExpanded()){
43777                 return;
43778             }
43779             this.list.hide();
43780             Roo.get(document).un('mousedown', this.collapseIf, this);
43781             Roo.get(document).un('mousewheel', this.collapseIf, this);
43782             this.fireEvent('collapse', this);
43783             this.validate();
43784         },
43785         
43786         expand : function()
43787         {
43788             Roo.log('expand');
43789
43790             if(this.isExpanded() || !this.hasFocus){
43791                 return;
43792             }
43793             
43794             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43795             this.list.setWidth(lw);
43796             
43797             this.list.show();
43798             this.restrictHeight();
43799             
43800             Roo.get(document).on('mousedown', this.collapseIf, this);
43801             Roo.get(document).on('mousewheel', this.collapseIf, this);
43802             
43803             this.fireEvent('expand', this);
43804         },
43805         
43806         restrictHeight : function()
43807         {
43808             this.list.alignTo(this.inputEl(), this.listAlign);
43809             this.list.alignTo(this.inputEl(), this.listAlign);
43810         },
43811         
43812         onViewOver : function(e, t)
43813         {
43814             if(this.inKeyMode){
43815                 return;
43816             }
43817             var item = this.view.findItemFromChild(t);
43818             
43819             if(item){
43820                 var index = this.view.indexOf(item);
43821                 this.select(index, false);
43822             }
43823         },
43824
43825         // private
43826         onViewClick : function(view, doFocus, el, e)
43827         {
43828             var index = this.view.getSelectedIndexes()[0];
43829             
43830             var r = this.store.getAt(index);
43831             
43832             if(r){
43833                 this.onSelect(r, index);
43834             }
43835             if(doFocus !== false && !this.blockFocus){
43836                 this.inputEl().focus();
43837             }
43838         },
43839         
43840         onViewMove : function(e, t)
43841         {
43842             this.inKeyMode = false;
43843         },
43844         
43845         select : function(index, scrollIntoView)
43846         {
43847             this.selectedIndex = index;
43848             this.view.select(index);
43849             if(scrollIntoView !== false){
43850                 var el = this.view.getNode(index);
43851                 if(el){
43852                     this.list.scrollChildIntoView(el, false);
43853                 }
43854             }
43855         },
43856         
43857         createList : function()
43858         {
43859             this.list = Roo.get(document.body).createChild({
43860                 tag: 'ul',
43861                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43862                 style: 'display:none'
43863             });
43864             
43865             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43866         },
43867         
43868         collapseIf : function(e)
43869         {
43870             var in_combo  = e.within(this.el);
43871             var in_list =  e.within(this.list);
43872             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43873             
43874             if (in_combo || in_list || is_list) {
43875                 return;
43876             }
43877             this.collapse();
43878         },
43879         
43880         onSelect : function(record, index)
43881         {
43882             if(this.fireEvent('beforeselect', this, record, index) !== false){
43883                 
43884                 this.setFlagClass(record.data.iso2);
43885                 this.setDialCode(record.data.dialCode);
43886                 this.hasFocus = false;
43887                 this.collapse();
43888                 this.fireEvent('select', this, record, index);
43889             }
43890         },
43891         
43892         flagEl : function()
43893         {
43894             var flag = this.el.select('div.flag',true).first();
43895             if(!flag){
43896                 return false;
43897             }
43898             return flag;
43899         },
43900         
43901         dialCodeHolderEl : function()
43902         {
43903             var d = this.el.select('input.dial-code-holder',true).first();
43904             if(!d){
43905                 return false;
43906             }
43907             return d;
43908         },
43909         
43910         setDialCode : function(v)
43911         {
43912             this.dialCodeHolder.dom.value = '+'+v;
43913         },
43914         
43915         setFlagClass : function(n)
43916         {
43917             this.flag.dom.className = 'flag '+n;
43918         },
43919         
43920         getValue : function()
43921         {
43922             var v = this.inputEl().getValue();
43923             if(this.dialCodeHolder) {
43924                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43925             }
43926             return v;
43927         },
43928         
43929         setValue : function(v)
43930         {
43931             var d = this.getDialCode(v);
43932             
43933             //invalid dial code
43934             if(v.length == 0 || !d || d.length == 0) {
43935                 if(this.rendered){
43936                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43937                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43938                 }
43939                 return;
43940             }
43941             
43942             //valid dial code
43943             this.setFlagClass(this.dialCodeMapping[d].iso2);
43944             this.setDialCode(d);
43945             this.inputEl().dom.value = v.replace('+'+d,'');
43946             this.hiddenEl().dom.value = this.getValue();
43947             
43948             this.validate();
43949         },
43950         
43951         getDialCode : function(v)
43952         {
43953             v = v ||  '';
43954             
43955             if (v.length == 0) {
43956                 return this.dialCodeHolder.dom.value;
43957             }
43958             
43959             var dialCode = "";
43960             if (v.charAt(0) != "+") {
43961                 return false;
43962             }
43963             var numericChars = "";
43964             for (var i = 1; i < v.length; i++) {
43965               var c = v.charAt(i);
43966               if (!isNaN(c)) {
43967                 numericChars += c;
43968                 if (this.dialCodeMapping[numericChars]) {
43969                   dialCode = v.substr(1, i);
43970                 }
43971                 if (numericChars.length == 4) {
43972                   break;
43973                 }
43974               }
43975             }
43976             return dialCode;
43977         },
43978         
43979         reset : function()
43980         {
43981             this.setValue(this.defaultDialCode);
43982             this.validate();
43983         },
43984         
43985         hiddenEl : function()
43986         {
43987             return this.el.select('input.hidden-tel-input',true).first();
43988         },
43989         
43990         // after setting val
43991         onKeyUp : function(e){
43992             this.setValue(this.getValue());
43993         },
43994         
43995         onKeyPress : function(e){
43996             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43997                 e.stopEvent();
43998             }
43999         }
44000         
44001 });
44002 /**
44003  * @class Roo.bootstrap.MoneyField
44004  * @extends Roo.bootstrap.ComboBox
44005  * Bootstrap MoneyField class
44006  * 
44007  * @constructor
44008  * Create a new MoneyField.
44009  * @param {Object} config Configuration options
44010  */
44011
44012 Roo.bootstrap.MoneyField = function(config) {
44013     
44014     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44015     
44016 };
44017
44018 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44019     
44020     /**
44021      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44022      */
44023     allowDecimals : true,
44024     /**
44025      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44026      */
44027     decimalSeparator : ".",
44028     /**
44029      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44030      */
44031     decimalPrecision : 0,
44032     /**
44033      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44034      */
44035     allowNegative : true,
44036     /**
44037      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44038      */
44039     allowZero: true,
44040     /**
44041      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44042      */
44043     minValue : Number.NEGATIVE_INFINITY,
44044     /**
44045      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44046      */
44047     maxValue : Number.MAX_VALUE,
44048     /**
44049      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44050      */
44051     minText : "The minimum value for this field is {0}",
44052     /**
44053      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44054      */
44055     maxText : "The maximum value for this field is {0}",
44056     /**
44057      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44058      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44059      */
44060     nanText : "{0} is not a valid number",
44061     /**
44062      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44063      */
44064     castInt : true,
44065     /**
44066      * @cfg {String} defaults currency of the MoneyField
44067      * value should be in lkey
44068      */
44069     defaultCurrency : false,
44070     /**
44071      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44072      */
44073     thousandsDelimiter : false,
44074     /**
44075      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44076      */
44077     max_length: false,
44078     
44079     inputlg : 9,
44080     inputmd : 9,
44081     inputsm : 9,
44082     inputxs : 6,
44083     
44084     store : false,
44085     
44086     getAutoCreate : function()
44087     {
44088         var align = this.labelAlign || this.parentLabelAlign();
44089         
44090         var id = Roo.id();
44091
44092         var cfg = {
44093             cls: 'form-group',
44094             cn: []
44095         };
44096
44097         var input =  {
44098             tag: 'input',
44099             id : id,
44100             cls : 'form-control roo-money-amount-input',
44101             autocomplete: 'new-password'
44102         };
44103         
44104         var hiddenInput = {
44105             tag: 'input',
44106             type: 'hidden',
44107             id: Roo.id(),
44108             cls: 'hidden-number-input'
44109         };
44110         
44111         if(this.max_length) {
44112             input.maxlength = this.max_length; 
44113         }
44114         
44115         if (this.name) {
44116             hiddenInput.name = this.name;
44117         }
44118
44119         if (this.disabled) {
44120             input.disabled = true;
44121         }
44122
44123         var clg = 12 - this.inputlg;
44124         var cmd = 12 - this.inputmd;
44125         var csm = 12 - this.inputsm;
44126         var cxs = 12 - this.inputxs;
44127         
44128         var container = {
44129             tag : 'div',
44130             cls : 'row roo-money-field',
44131             cn : [
44132                 {
44133                     tag : 'div',
44134                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44135                     cn : [
44136                         {
44137                             tag : 'div',
44138                             cls: 'roo-select2-container input-group',
44139                             cn: [
44140                                 {
44141                                     tag : 'input',
44142                                     cls : 'form-control roo-money-currency-input',
44143                                     autocomplete: 'new-password',
44144                                     readOnly : 1,
44145                                     name : this.currencyName
44146                                 },
44147                                 {
44148                                     tag :'span',
44149                                     cls : 'input-group-addon',
44150                                     cn : [
44151                                         {
44152                                             tag: 'span',
44153                                             cls: 'caret'
44154                                         }
44155                                     ]
44156                                 }
44157                             ]
44158                         }
44159                     ]
44160                 },
44161                 {
44162                     tag : 'div',
44163                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44164                     cn : [
44165                         {
44166                             tag: 'div',
44167                             cls: this.hasFeedback ? 'has-feedback' : '',
44168                             cn: [
44169                                 input
44170                             ]
44171                         }
44172                     ]
44173                 }
44174             ]
44175             
44176         };
44177         
44178         if (this.fieldLabel.length) {
44179             var indicator = {
44180                 tag: 'i',
44181                 tooltip: 'This field is required'
44182             };
44183
44184             var label = {
44185                 tag: 'label',
44186                 'for':  id,
44187                 cls: 'control-label',
44188                 cn: []
44189             };
44190
44191             var label_text = {
44192                 tag: 'span',
44193                 html: this.fieldLabel
44194             };
44195
44196             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44197             label.cn = [
44198                 indicator,
44199                 label_text
44200             ];
44201
44202             if(this.indicatorpos == 'right') {
44203                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44204                 label.cn = [
44205                     label_text,
44206                     indicator
44207                 ];
44208             }
44209
44210             if(align == 'left') {
44211                 container = {
44212                     tag: 'div',
44213                     cn: [
44214                         container
44215                     ]
44216                 };
44217
44218                 if(this.labelWidth > 12){
44219                     label.style = "width: " + this.labelWidth + 'px';
44220                 }
44221                 if(this.labelWidth < 13 && this.labelmd == 0){
44222                     this.labelmd = this.labelWidth;
44223                 }
44224                 if(this.labellg > 0){
44225                     label.cls += ' col-lg-' + this.labellg;
44226                     input.cls += ' col-lg-' + (12 - this.labellg);
44227                 }
44228                 if(this.labelmd > 0){
44229                     label.cls += ' col-md-' + this.labelmd;
44230                     container.cls += ' col-md-' + (12 - this.labelmd);
44231                 }
44232                 if(this.labelsm > 0){
44233                     label.cls += ' col-sm-' + this.labelsm;
44234                     container.cls += ' col-sm-' + (12 - this.labelsm);
44235                 }
44236                 if(this.labelxs > 0){
44237                     label.cls += ' col-xs-' + this.labelxs;
44238                     container.cls += ' col-xs-' + (12 - this.labelxs);
44239                 }
44240             }
44241         }
44242
44243         cfg.cn = [
44244             label,
44245             container,
44246             hiddenInput
44247         ];
44248         
44249         var settings = this;
44250
44251         ['xs','sm','md','lg'].map(function(size){
44252             if (settings[size]) {
44253                 cfg.cls += ' col-' + size + '-' + settings[size];
44254             }
44255         });
44256         
44257         return cfg;
44258     },
44259     
44260     initEvents : function()
44261     {
44262         this.indicator = this.indicatorEl();
44263         
44264         this.initCurrencyEvent();
44265         
44266         this.initNumberEvent();
44267     },
44268     
44269     initCurrencyEvent : function()
44270     {
44271         if (!this.store) {
44272             throw "can not find store for combo";
44273         }
44274         
44275         this.store = Roo.factory(this.store, Roo.data);
44276         this.store.parent = this;
44277         
44278         this.createList();
44279         
44280         this.triggerEl = this.el.select('.input-group-addon', true).first();
44281         
44282         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44283         
44284         var _this = this;
44285         
44286         (function(){
44287             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44288             _this.list.setWidth(lw);
44289         }).defer(100);
44290         
44291         this.list.on('mouseover', this.onViewOver, this);
44292         this.list.on('mousemove', this.onViewMove, this);
44293         this.list.on('scroll', this.onViewScroll, this);
44294         
44295         if(!this.tpl){
44296             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44297         }
44298         
44299         this.view = new Roo.View(this.list, this.tpl, {
44300             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44301         });
44302         
44303         this.view.on('click', this.onViewClick, this);
44304         
44305         this.store.on('beforeload', this.onBeforeLoad, this);
44306         this.store.on('load', this.onLoad, this);
44307         this.store.on('loadexception', this.onLoadException, this);
44308         
44309         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44310             "up" : function(e){
44311                 this.inKeyMode = true;
44312                 this.selectPrev();
44313             },
44314
44315             "down" : function(e){
44316                 if(!this.isExpanded()){
44317                     this.onTriggerClick();
44318                 }else{
44319                     this.inKeyMode = true;
44320                     this.selectNext();
44321                 }
44322             },
44323
44324             "enter" : function(e){
44325                 this.collapse();
44326                 
44327                 if(this.fireEvent("specialkey", this, e)){
44328                     this.onViewClick(false);
44329                 }
44330                 
44331                 return true;
44332             },
44333
44334             "esc" : function(e){
44335                 this.collapse();
44336             },
44337
44338             "tab" : function(e){
44339                 this.collapse();
44340                 
44341                 if(this.fireEvent("specialkey", this, e)){
44342                     this.onViewClick(false);
44343                 }
44344                 
44345                 return true;
44346             },
44347
44348             scope : this,
44349
44350             doRelay : function(foo, bar, hname){
44351                 if(hname == 'down' || this.scope.isExpanded()){
44352                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44353                 }
44354                 return true;
44355             },
44356
44357             forceKeyDown: true
44358         });
44359         
44360         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44361         
44362     },
44363     
44364     initNumberEvent : function(e)
44365     {
44366         this.inputEl().on("keydown" , this.fireKey,  this);
44367         this.inputEl().on("focus", this.onFocus,  this);
44368         this.inputEl().on("blur", this.onBlur,  this);
44369         
44370         this.inputEl().relayEvent('keyup', this);
44371         
44372         if(this.indicator){
44373             this.indicator.addClass('invisible');
44374         }
44375  
44376         this.originalValue = this.getValue();
44377         
44378         if(this.validationEvent == 'keyup'){
44379             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44380             this.inputEl().on('keyup', this.filterValidation, this);
44381         }
44382         else if(this.validationEvent !== false){
44383             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44384         }
44385         
44386         if(this.selectOnFocus){
44387             this.on("focus", this.preFocus, this);
44388             
44389         }
44390         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44391             this.inputEl().on("keypress", this.filterKeys, this);
44392         } else {
44393             this.inputEl().relayEvent('keypress', this);
44394         }
44395         
44396         var allowed = "0123456789";
44397         
44398         if(this.allowDecimals){
44399             allowed += this.decimalSeparator;
44400         }
44401         
44402         if(this.allowNegative){
44403             allowed += "-";
44404         }
44405         
44406         if(this.thousandsDelimiter) {
44407             allowed += ",";
44408         }
44409         
44410         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44411         
44412         var keyPress = function(e){
44413             
44414             var k = e.getKey();
44415             
44416             var c = e.getCharCode();
44417             
44418             if(
44419                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44420                     allowed.indexOf(String.fromCharCode(c)) === -1
44421             ){
44422                 e.stopEvent();
44423                 return;
44424             }
44425             
44426             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44427                 return;
44428             }
44429             
44430             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44431                 e.stopEvent();
44432             }
44433         };
44434         
44435         this.inputEl().on("keypress", keyPress, this);
44436         
44437     },
44438     
44439     onTriggerClick : function(e)
44440     {   
44441         if(this.disabled){
44442             return;
44443         }
44444         
44445         this.page = 0;
44446         this.loadNext = false;
44447         
44448         if(this.isExpanded()){
44449             this.collapse();
44450             return;
44451         }
44452         
44453         this.hasFocus = true;
44454         
44455         if(this.triggerAction == 'all') {
44456             this.doQuery(this.allQuery, true);
44457             return;
44458         }
44459         
44460         this.doQuery(this.getRawValue());
44461     },
44462     
44463     getCurrency : function()
44464     {   
44465         var v = this.currencyEl().getValue();
44466         
44467         return v;
44468     },
44469     
44470     restrictHeight : function()
44471     {
44472         this.list.alignTo(this.currencyEl(), this.listAlign);
44473         this.list.alignTo(this.currencyEl(), this.listAlign);
44474     },
44475     
44476     onViewClick : function(view, doFocus, el, e)
44477     {
44478         var index = this.view.getSelectedIndexes()[0];
44479         
44480         var r = this.store.getAt(index);
44481         
44482         if(r){
44483             this.onSelect(r, index);
44484         }
44485     },
44486     
44487     onSelect : function(record, index){
44488         
44489         if(this.fireEvent('beforeselect', this, record, index) !== false){
44490         
44491             this.setFromCurrencyData(index > -1 ? record.data : false);
44492             
44493             this.collapse();
44494             
44495             this.fireEvent('select', this, record, index);
44496         }
44497     },
44498     
44499     setFromCurrencyData : function(o)
44500     {
44501         var currency = '';
44502         
44503         this.lastCurrency = o;
44504         
44505         if (this.currencyField) {
44506             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44507         } else {
44508             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44509         }
44510         
44511         this.lastSelectionText = currency;
44512         
44513         //setting default currency
44514         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44515             this.setCurrency(this.defaultCurrency);
44516             return;
44517         }
44518         
44519         this.setCurrency(currency);
44520     },
44521     
44522     setFromData : function(o)
44523     {
44524         var c = {};
44525         
44526         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44527         
44528         this.setFromCurrencyData(c);
44529         
44530         var value = '';
44531         
44532         if (this.name) {
44533             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44534         } else {
44535             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44536         }
44537         
44538         this.setValue(value);
44539         
44540     },
44541     
44542     setCurrency : function(v)
44543     {   
44544         this.currencyValue = v;
44545         
44546         if(this.rendered){
44547             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44548             this.validate();
44549         }
44550     },
44551     
44552     setValue : function(v)
44553     {
44554         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44555         
44556         this.value = v;
44557         
44558         if(this.rendered){
44559             
44560             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44561             
44562             this.inputEl().dom.value = (v == '') ? '' :
44563                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44564             
44565             if(!this.allowZero && v === '0') {
44566                 this.hiddenEl().dom.value = '';
44567                 this.inputEl().dom.value = '';
44568             }
44569             
44570             this.validate();
44571         }
44572     },
44573     
44574     getRawValue : function()
44575     {
44576         var v = this.inputEl().getValue();
44577         
44578         return v;
44579     },
44580     
44581     getValue : function()
44582     {
44583         return this.fixPrecision(this.parseValue(this.getRawValue()));
44584     },
44585     
44586     parseValue : function(value)
44587     {
44588         if(this.thousandsDelimiter) {
44589             value += "";
44590             r = new RegExp(",", "g");
44591             value = value.replace(r, "");
44592         }
44593         
44594         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44595         return isNaN(value) ? '' : value;
44596         
44597     },
44598     
44599     fixPrecision : function(value)
44600     {
44601         if(this.thousandsDelimiter) {
44602             value += "";
44603             r = new RegExp(",", "g");
44604             value = value.replace(r, "");
44605         }
44606         
44607         var nan = isNaN(value);
44608         
44609         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44610             return nan ? '' : value;
44611         }
44612         return parseFloat(value).toFixed(this.decimalPrecision);
44613     },
44614     
44615     decimalPrecisionFcn : function(v)
44616     {
44617         return Math.floor(v);
44618     },
44619     
44620     validateValue : function(value)
44621     {
44622         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44623             return false;
44624         }
44625         
44626         var num = this.parseValue(value);
44627         
44628         if(isNaN(num)){
44629             this.markInvalid(String.format(this.nanText, value));
44630             return false;
44631         }
44632         
44633         if(num < this.minValue){
44634             this.markInvalid(String.format(this.minText, this.minValue));
44635             return false;
44636         }
44637         
44638         if(num > this.maxValue){
44639             this.markInvalid(String.format(this.maxText, this.maxValue));
44640             return false;
44641         }
44642         
44643         return true;
44644     },
44645     
44646     validate : function()
44647     {
44648         if(this.disabled || this.allowBlank){
44649             this.markValid();
44650             return true;
44651         }
44652         
44653         var currency = this.getCurrency();
44654         
44655         if(this.validateValue(this.getRawValue()) && currency.length){
44656             this.markValid();
44657             return true;
44658         }
44659         
44660         this.markInvalid();
44661         return false;
44662     },
44663     
44664     getName: function()
44665     {
44666         return this.name;
44667     },
44668     
44669     beforeBlur : function()
44670     {
44671         if(!this.castInt){
44672             return;
44673         }
44674         
44675         var v = this.parseValue(this.getRawValue());
44676         
44677         if(v || v == 0){
44678             this.setValue(v);
44679         }
44680     },
44681     
44682     onBlur : function()
44683     {
44684         this.beforeBlur();
44685         
44686         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44687             //this.el.removeClass(this.focusClass);
44688         }
44689         
44690         this.hasFocus = false;
44691         
44692         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44693             this.validate();
44694         }
44695         
44696         var v = this.getValue();
44697         
44698         if(String(v) !== String(this.startValue)){
44699             this.fireEvent('change', this, v, this.startValue);
44700         }
44701         
44702         this.fireEvent("blur", this);
44703     },
44704     
44705     inputEl : function()
44706     {
44707         return this.el.select('.roo-money-amount-input', true).first();
44708     },
44709     
44710     currencyEl : function()
44711     {
44712         return this.el.select('.roo-money-currency-input', true).first();
44713     },
44714     
44715     hiddenEl : function()
44716     {
44717         return this.el.select('input.hidden-number-input',true).first();
44718     }
44719     
44720 });/**
44721  * @class Roo.bootstrap.BezierSignature
44722  * @extends Roo.bootstrap.Component
44723  * Bootstrap BezierSignature class
44724  * This script refer to:
44725  *    Title: Signature Pad
44726  *    Author: szimek
44727  *    Availability: https://github.com/szimek/signature_pad
44728  *
44729  * @constructor
44730  * Create a new BezierSignature
44731  * @param {Object} config The config object
44732  */
44733
44734 Roo.bootstrap.BezierSignature = function(config){
44735     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44736     this.addEvents({
44737         "resize" : true
44738     });
44739 };
44740
44741 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44742 {
44743      
44744     curve_data: [],
44745     
44746     is_empty: true,
44747     
44748     mouse_btn_down: true,
44749     
44750     /**
44751      * @cfg {int} canvas height
44752      */
44753     canvas_height: '200px',
44754     
44755     /**
44756      * @cfg {float|function} Radius of a single dot.
44757      */ 
44758     dot_size: false,
44759     
44760     /**
44761      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44762      */
44763     min_width: 0.5,
44764     
44765     /**
44766      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44767      */
44768     max_width: 2.5,
44769     
44770     /**
44771      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44772      */
44773     throttle: 16,
44774     
44775     /**
44776      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44777      */
44778     min_distance: 5,
44779     
44780     /**
44781      * @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.
44782      */
44783     bg_color: 'rgba(0, 0, 0, 0)',
44784     
44785     /**
44786      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44787      */
44788     dot_color: 'black',
44789     
44790     /**
44791      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44792      */ 
44793     velocity_filter_weight: 0.7,
44794     
44795     /**
44796      * @cfg {function} Callback when stroke begin. 
44797      */
44798     onBegin: false,
44799     
44800     /**
44801      * @cfg {function} Callback when stroke end.
44802      */
44803     onEnd: false,
44804     
44805     getAutoCreate : function()
44806     {
44807         var cls = 'roo-signature column';
44808         
44809         if(this.cls){
44810             cls += ' ' + this.cls;
44811         }
44812         
44813         var col_sizes = [
44814             'lg',
44815             'md',
44816             'sm',
44817             'xs'
44818         ];
44819         
44820         for(var i = 0; i < col_sizes.length; i++) {
44821             if(this[col_sizes[i]]) {
44822                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44823             }
44824         }
44825         
44826         var cfg = {
44827             tag: 'div',
44828             cls: cls,
44829             cn: [
44830                 {
44831                     tag: 'div',
44832                     cls: 'roo-signature-body',
44833                     cn: [
44834                         {
44835                             tag: 'canvas',
44836                             cls: 'roo-signature-body-canvas',
44837                             height: this.canvas_height,
44838                             width: this.canvas_width
44839                         }
44840                     ]
44841                 },
44842                 {
44843                     tag: 'input',
44844                     type: 'file',
44845                     style: 'display: none'
44846                 }
44847             ]
44848         };
44849         
44850         return cfg;
44851     },
44852     
44853     initEvents: function() 
44854     {
44855         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44856         
44857         var canvas = this.canvasEl();
44858         
44859         // mouse && touch event swapping...
44860         canvas.dom.style.touchAction = 'none';
44861         canvas.dom.style.msTouchAction = 'none';
44862         
44863         this.mouse_btn_down = false;
44864         canvas.on('mousedown', this._handleMouseDown, this);
44865         canvas.on('mousemove', this._handleMouseMove, this);
44866         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44867         
44868         if (window.PointerEvent) {
44869             canvas.on('pointerdown', this._handleMouseDown, this);
44870             canvas.on('pointermove', this._handleMouseMove, this);
44871             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44872         }
44873         
44874         if ('ontouchstart' in window) {
44875             canvas.on('touchstart', this._handleTouchStart, this);
44876             canvas.on('touchmove', this._handleTouchMove, this);
44877             canvas.on('touchend', this._handleTouchEnd, this);
44878         }
44879         
44880         Roo.EventManager.onWindowResize(this.resize, this, true);
44881         
44882         // file input event
44883         this.fileEl().on('change', this.uploadImage, this);
44884         
44885         this.clear();
44886         
44887         this.resize();
44888     },
44889     
44890     resize: function(){
44891         
44892         var canvas = this.canvasEl().dom;
44893         var ctx = this.canvasElCtx();
44894         var img_data = false;
44895         
44896         if(canvas.width > 0) {
44897             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44898         }
44899         // setting canvas width will clean img data
44900         canvas.width = 0;
44901         
44902         var style = window.getComputedStyle ? 
44903             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44904             
44905         var padding_left = parseInt(style.paddingLeft) || 0;
44906         var padding_right = parseInt(style.paddingRight) || 0;
44907         
44908         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44909         
44910         if(img_data) {
44911             ctx.putImageData(img_data, 0, 0);
44912         }
44913     },
44914     
44915     _handleMouseDown: function(e)
44916     {
44917         if (e.browserEvent.which === 1) {
44918             this.mouse_btn_down = true;
44919             this.strokeBegin(e);
44920         }
44921     },
44922     
44923     _handleMouseMove: function (e)
44924     {
44925         if (this.mouse_btn_down) {
44926             this.strokeMoveUpdate(e);
44927         }
44928     },
44929     
44930     _handleMouseUp: function (e)
44931     {
44932         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44933             this.mouse_btn_down = false;
44934             this.strokeEnd(e);
44935         }
44936     },
44937     
44938     _handleTouchStart: function (e) {
44939         
44940         e.preventDefault();
44941         if (e.browserEvent.targetTouches.length === 1) {
44942             // var touch = e.browserEvent.changedTouches[0];
44943             // this.strokeBegin(touch);
44944             
44945              this.strokeBegin(e); // assume e catching the correct xy...
44946         }
44947     },
44948     
44949     _handleTouchMove: function (e) {
44950         e.preventDefault();
44951         // var touch = event.targetTouches[0];
44952         // _this._strokeMoveUpdate(touch);
44953         this.strokeMoveUpdate(e);
44954     },
44955     
44956     _handleTouchEnd: function (e) {
44957         var wasCanvasTouched = e.target === this.canvasEl().dom;
44958         if (wasCanvasTouched) {
44959             e.preventDefault();
44960             // var touch = event.changedTouches[0];
44961             // _this._strokeEnd(touch);
44962             this.strokeEnd(e);
44963         }
44964     },
44965     
44966     reset: function () {
44967         this._lastPoints = [];
44968         this._lastVelocity = 0;
44969         this._lastWidth = (this.min_width + this.max_width) / 2;
44970         this.canvasElCtx().fillStyle = this.dot_color;
44971     },
44972     
44973     strokeMoveUpdate: function(e)
44974     {
44975         this.strokeUpdate(e);
44976         
44977         if (this.throttle) {
44978             this.throttleStroke(this.strokeUpdate, this.throttle);
44979         }
44980         else {
44981             this.strokeUpdate(e);
44982         }
44983     },
44984     
44985     strokeBegin: function(e)
44986     {
44987         var newPointGroup = {
44988             color: this.dot_color,
44989             points: []
44990         };
44991         
44992         if (typeof this.onBegin === 'function') {
44993             this.onBegin(e);
44994         }
44995         
44996         this.curve_data.push(newPointGroup);
44997         this.reset();
44998         this.strokeUpdate(e);
44999     },
45000     
45001     strokeUpdate: function(e)
45002     {
45003         var rect = this.canvasEl().dom.getBoundingClientRect();
45004         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45005         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45006         var lastPoints = lastPointGroup.points;
45007         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45008         var isLastPointTooClose = lastPoint
45009             ? point.distanceTo(lastPoint) <= this.min_distance
45010             : false;
45011         var color = lastPointGroup.color;
45012         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45013             var curve = this.addPoint(point);
45014             if (!lastPoint) {
45015                 this.drawDot({color: color, point: point});
45016             }
45017             else if (curve) {
45018                 this.drawCurve({color: color, curve: curve});
45019             }
45020             lastPoints.push({
45021                 time: point.time,
45022                 x: point.x,
45023                 y: point.y
45024             });
45025         }
45026     },
45027     
45028     strokeEnd: function(e)
45029     {
45030         this.strokeUpdate(e);
45031         if (typeof this.onEnd === 'function') {
45032             this.onEnd(e);
45033         }
45034     },
45035     
45036     addPoint:  function (point) {
45037         var _lastPoints = this._lastPoints;
45038         _lastPoints.push(point);
45039         if (_lastPoints.length > 2) {
45040             if (_lastPoints.length === 3) {
45041                 _lastPoints.unshift(_lastPoints[0]);
45042             }
45043             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45044             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45045             _lastPoints.shift();
45046             return curve;
45047         }
45048         return null;
45049     },
45050     
45051     calculateCurveWidths: function (startPoint, endPoint) {
45052         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45053             (1 - this.velocity_filter_weight) * this._lastVelocity;
45054
45055         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45056         var widths = {
45057             end: newWidth,
45058             start: this._lastWidth
45059         };
45060         
45061         this._lastVelocity = velocity;
45062         this._lastWidth = newWidth;
45063         return widths;
45064     },
45065     
45066     drawDot: function (_a) {
45067         var color = _a.color, point = _a.point;
45068         var ctx = this.canvasElCtx();
45069         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45070         ctx.beginPath();
45071         this.drawCurveSegment(point.x, point.y, width);
45072         ctx.closePath();
45073         ctx.fillStyle = color;
45074         ctx.fill();
45075     },
45076     
45077     drawCurve: function (_a) {
45078         var color = _a.color, curve = _a.curve;
45079         var ctx = this.canvasElCtx();
45080         var widthDelta = curve.endWidth - curve.startWidth;
45081         var drawSteps = Math.floor(curve.length()) * 2;
45082         ctx.beginPath();
45083         ctx.fillStyle = color;
45084         for (var i = 0; i < drawSteps; i += 1) {
45085         var t = i / drawSteps;
45086         var tt = t * t;
45087         var ttt = tt * t;
45088         var u = 1 - t;
45089         var uu = u * u;
45090         var uuu = uu * u;
45091         var x = uuu * curve.startPoint.x;
45092         x += 3 * uu * t * curve.control1.x;
45093         x += 3 * u * tt * curve.control2.x;
45094         x += ttt * curve.endPoint.x;
45095         var y = uuu * curve.startPoint.y;
45096         y += 3 * uu * t * curve.control1.y;
45097         y += 3 * u * tt * curve.control2.y;
45098         y += ttt * curve.endPoint.y;
45099         var width = curve.startWidth + ttt * widthDelta;
45100         this.drawCurveSegment(x, y, width);
45101         }
45102         ctx.closePath();
45103         ctx.fill();
45104     },
45105     
45106     drawCurveSegment: function (x, y, width) {
45107         var ctx = this.canvasElCtx();
45108         ctx.moveTo(x, y);
45109         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45110         this.is_empty = false;
45111     },
45112     
45113     clear: function()
45114     {
45115         var ctx = this.canvasElCtx();
45116         var canvas = this.canvasEl().dom;
45117         ctx.fillStyle = this.bg_color;
45118         ctx.clearRect(0, 0, canvas.width, canvas.height);
45119         ctx.fillRect(0, 0, canvas.width, canvas.height);
45120         this.curve_data = [];
45121         this.reset();
45122         this.is_empty = true;
45123     },
45124     
45125     fileEl: function()
45126     {
45127         return  this.el.select('input',true).first();
45128     },
45129     
45130     canvasEl: function()
45131     {
45132         return this.el.select('canvas',true).first();
45133     },
45134     
45135     canvasElCtx: function()
45136     {
45137         return this.el.select('canvas',true).first().dom.getContext('2d');
45138     },
45139     
45140     getImage: function(type)
45141     {
45142         if(this.is_empty) {
45143             return false;
45144         }
45145         
45146         // encryption ?
45147         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45148     },
45149     
45150     drawFromImage: function(img_src)
45151     {
45152         var img = new Image();
45153         
45154         img.onload = function(){
45155             this.canvasElCtx().drawImage(img, 0, 0);
45156         }.bind(this);
45157         
45158         img.src = img_src;
45159         
45160         this.is_empty = false;
45161     },
45162     
45163     selectImage: function()
45164     {
45165         this.fileEl().dom.click();
45166     },
45167     
45168     uploadImage: function(e)
45169     {
45170         var reader = new FileReader();
45171         
45172         reader.onload = function(e){
45173             var img = new Image();
45174             img.onload = function(){
45175                 this.reset();
45176                 this.canvasElCtx().drawImage(img, 0, 0);
45177             }.bind(this);
45178             img.src = e.target.result;
45179         }.bind(this);
45180         
45181         reader.readAsDataURL(e.target.files[0]);
45182     },
45183     
45184     // Bezier Point Constructor
45185     Point: (function () {
45186         function Point(x, y, time) {
45187             this.x = x;
45188             this.y = y;
45189             this.time = time || Date.now();
45190         }
45191         Point.prototype.distanceTo = function (start) {
45192             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45193         };
45194         Point.prototype.equals = function (other) {
45195             return this.x === other.x && this.y === other.y && this.time === other.time;
45196         };
45197         Point.prototype.velocityFrom = function (start) {
45198             return this.time !== start.time
45199             ? this.distanceTo(start) / (this.time - start.time)
45200             : 0;
45201         };
45202         return Point;
45203     }()),
45204     
45205     
45206     // Bezier Constructor
45207     Bezier: (function () {
45208         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45209             this.startPoint = startPoint;
45210             this.control2 = control2;
45211             this.control1 = control1;
45212             this.endPoint = endPoint;
45213             this.startWidth = startWidth;
45214             this.endWidth = endWidth;
45215         }
45216         Bezier.fromPoints = function (points, widths, scope) {
45217             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45218             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45219             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45220         };
45221         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45222             var dx1 = s1.x - s2.x;
45223             var dy1 = s1.y - s2.y;
45224             var dx2 = s2.x - s3.x;
45225             var dy2 = s2.y - s3.y;
45226             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45227             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45228             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45229             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45230             var dxm = m1.x - m2.x;
45231             var dym = m1.y - m2.y;
45232             var k = l2 / (l1 + l2);
45233             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45234             var tx = s2.x - cm.x;
45235             var ty = s2.y - cm.y;
45236             return {
45237                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45238                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45239             };
45240         };
45241         Bezier.prototype.length = function () {
45242             var steps = 10;
45243             var length = 0;
45244             var px;
45245             var py;
45246             for (var i = 0; i <= steps; i += 1) {
45247                 var t = i / steps;
45248                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45249                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45250                 if (i > 0) {
45251                     var xdiff = cx - px;
45252                     var ydiff = cy - py;
45253                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45254                 }
45255                 px = cx;
45256                 py = cy;
45257             }
45258             return length;
45259         };
45260         Bezier.prototype.point = function (t, start, c1, c2, end) {
45261             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45262             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45263             + (3.0 * c2 * (1.0 - t) * t * t)
45264             + (end * t * t * t);
45265         };
45266         return Bezier;
45267     }()),
45268     
45269     throttleStroke: function(fn, wait) {
45270       if (wait === void 0) { wait = 250; }
45271       var previous = 0;
45272       var timeout = null;
45273       var result;
45274       var storedContext;
45275       var storedArgs;
45276       var later = function () {
45277           previous = Date.now();
45278           timeout = null;
45279           result = fn.apply(storedContext, storedArgs);
45280           if (!timeout) {
45281               storedContext = null;
45282               storedArgs = [];
45283           }
45284       };
45285       return function wrapper() {
45286           var args = [];
45287           for (var _i = 0; _i < arguments.length; _i++) {
45288               args[_i] = arguments[_i];
45289           }
45290           var now = Date.now();
45291           var remaining = wait - (now - previous);
45292           storedContext = this;
45293           storedArgs = args;
45294           if (remaining <= 0 || remaining > wait) {
45295               if (timeout) {
45296                   clearTimeout(timeout);
45297                   timeout = null;
45298               }
45299               previous = now;
45300               result = fn.apply(storedContext, storedArgs);
45301               if (!timeout) {
45302                   storedContext = null;
45303                   storedArgs = [];
45304               }
45305           }
45306           else if (!timeout) {
45307               timeout = window.setTimeout(later, remaining);
45308           }
45309           return result;
45310       };
45311   }
45312   
45313 });
45314
45315  
45316
45317