c30b5b554cfcbd6ee1b57f12640bd0c61b40f1e8
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<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>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
965  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size ( lg | sm | xs)
968  * @cfg {String} tag ( a | input | submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     this.weightClass = ["btn-default btn-outline-secondary", 
994                        "btn-primary", 
995                        "btn-success", 
996                        "btn-info", 
997                        "btn-warning",
998                        "btn-danger",
999                        "btn-link"
1000                       ],  
1001     this.addEvents({
1002         // raw events
1003         /**
1004          * @event click
1005          * When a butotn is pressed
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          */
1009         "click" : true,
1010          /**
1011          * @event toggle
1012          * After the button has been toggles
1013          * @param {Roo.bootstrap.Button} btn
1014          * @param {Roo.EventObject} e
1015          * @param {boolean} pressed (also available as button.pressed)
1016          */
1017         "toggle" : true
1018     });
1019 };
1020
1021 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1022     html: false,
1023     active: false,
1024     weight: '',
1025     badge_weight: '',
1026     outline : false,
1027     size: '',
1028     tag: 'button',
1029     href: '',
1030     disabled: false,
1031     isClose: false,
1032     glyphicon: '',
1033     fa: '',
1034     badge: '',
1035     theme: 'default',
1036     inverse: false,
1037     
1038     toggle: false,
1039     ontext: 'ON',
1040     offtext: 'OFF',
1041     defaulton: true,
1042     preventDefault: true,
1043     removeClass: false,
1044     name: false,
1045     target: false,
1046      
1047     pressed : null,
1048      
1049     
1050     getAutoCreate : function(){
1051         
1052         var cfg = {
1053             tag : 'button',
1054             cls : 'roo-button',
1055             html: ''
1056         };
1057         
1058         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1059             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1060             this.tag = 'button';
1061         } else {
1062             cfg.tag = this.tag;
1063         }
1064         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1065         
1066         if (this.toggle == true) {
1067             cfg={
1068                 tag: 'div',
1069                 cls: 'slider-frame roo-button',
1070                 cn: [
1071                     {
1072                         tag: 'span',
1073                         'data-on-text':'ON',
1074                         'data-off-text':'OFF',
1075                         cls: 'slider-button',
1076                         html: this.offtext
1077                     }
1078                 ]
1079             };
1080             
1081             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1082                 cfg.cls += ' '+this.weight;
1083             }
1084             
1085             return cfg;
1086         }
1087         
1088         if (this.isClose) {
1089             cfg.cls += ' close';
1090             
1091             cfg["aria-hidden"] = true;
1092             
1093             cfg.html = "&times;";
1094             
1095             return cfg;
1096         }
1097         
1098          
1099         if (this.theme==='default') {
1100             cfg.cls = 'btn roo-button';
1101             
1102             //if (this.parentType != 'Navbar') {
1103             this.weight = this.weight.length ?  this.weight : 'default';
1104             //}
1105             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1106                 
1107                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1108                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1109                 cfg.cls += ' btn-' + outline + weight;
1110                 if (this.weight == 'default') {
1111                     // BC
1112                     cfg.cls += ' btn-' + this.weight;
1113                 }
1114             }
1115         } else if (this.theme==='glow') {
1116             
1117             cfg.tag = 'a';
1118             cfg.cls = 'btn-glow roo-button';
1119             
1120             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1121                 
1122                 cfg.cls += ' ' + this.weight;
1123             }
1124         }
1125    
1126         
1127         if (this.inverse) {
1128             this.cls += ' inverse';
1129         }
1130         
1131         
1132         if (this.active || this.pressed === true) {
1133             cfg.cls += ' active';
1134         }
1135         
1136         if (this.disabled) {
1137             cfg.disabled = 'disabled';
1138         }
1139         
1140         if (this.items) {
1141             Roo.log('changing to ul' );
1142             cfg.tag = 'ul';
1143             this.glyphicon = 'caret';
1144             if (Roo.bootstrap.version == 4) {
1145                 this.fa = 'caret-down';
1146             }
1147             
1148         }
1149         
1150         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1151          
1152         //gsRoo.log(this.parentType);
1153         if (this.parentType === 'Navbar' && !this.parent().bar) {
1154             Roo.log('changing to li?');
1155             
1156             cfg.tag = 'li';
1157             
1158             cfg.cls = '';
1159             cfg.cn =  [{
1160                 tag : 'a',
1161                 cls : 'roo-button',
1162                 html : this.html,
1163                 href : this.href || '#'
1164             }];
1165             if (this.menu) {
1166                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1167                 cfg.cls += ' dropdown';
1168             }   
1169             
1170             delete cfg.html;
1171             
1172         }
1173         
1174        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1175         
1176         if (this.glyphicon) {
1177             cfg.html = ' ' + cfg.html;
1178             
1179             cfg.cn = [
1180                 {
1181                     tag: 'span',
1182                     cls: 'glyphicon glyphicon-' + this.glyphicon
1183                 }
1184             ];
1185         }
1186         if (this.fa) {
1187             cfg.html = ' ' + cfg.html;
1188             
1189             cfg.cn = [
1190                 {
1191                     tag: 'i',
1192                     cls: 'fa fas fa-' + this.fa
1193                 }
1194             ];
1195         }
1196         
1197         if (this.badge) {
1198             cfg.html += ' ';
1199             
1200             cfg.tag = 'a';
1201             
1202 //            cfg.cls='btn roo-button';
1203             
1204             cfg.href=this.href;
1205             
1206             var value = cfg.html;
1207             
1208             if(this.glyphicon){
1209                 value = {
1210                     tag: 'span',
1211                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1212                     html: this.html
1213                 };
1214             }
1215             if(this.fa){
1216                 value = {
1217                     tag: 'i',
1218                     cls: 'fa fas fa-' + this.fa,
1219                     html: this.html
1220                 };
1221             }
1222             
1223             var bw = this.badge_weight.length ? this.badge_weight :
1224                 (this.weight.length ? this.weight : 'secondary');
1225             bw = bw == 'default' ? 'secondary' : bw;
1226             
1227             cfg.cn = [
1228                 value,
1229                 {
1230                     tag: 'span',
1231                     cls: 'badge badge-' + bw,
1232                     html: this.badge
1233                 }
1234             ];
1235             
1236             cfg.html='';
1237         }
1238         
1239         if (this.menu) {
1240             cfg.cls += ' dropdown';
1241             cfg.html = typeof(cfg.html) != 'undefined' ?
1242                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1243         }
1244         
1245         if (cfg.tag !== 'a' && this.href !== '') {
1246             throw "Tag must be a to set href.";
1247         } else if (this.href.length > 0) {
1248             cfg.href = this.href;
1249         }
1250         
1251         if(this.removeClass){
1252             cfg.cls = '';
1253         }
1254         
1255         if(this.target){
1256             cfg.target = this.target;
1257         }
1258         
1259         return cfg;
1260     },
1261     initEvents: function() {
1262        // Roo.log('init events?');
1263 //        Roo.log(this.el.dom);
1264         // add the menu...
1265         
1266         if (typeof (this.menu) != 'undefined') {
1267             this.menu.parentType = this.xtype;
1268             this.menu.triggerEl = this.el;
1269             this.addxtype(Roo.apply({}, this.menu));
1270         }
1271
1272
1273        if (this.el.hasClass('roo-button')) {
1274             this.el.on('click', this.onClick, this);
1275        } else {
1276             this.el.select('.roo-button').on('click', this.onClick, this);
1277        }
1278        
1279        if(this.removeClass){
1280            this.el.on('click', this.onClick, this);
1281        }
1282        
1283        this.el.enableDisplayMode();
1284         
1285     },
1286     onClick : function(e)
1287     {
1288         if (this.disabled) {
1289             return;
1290         }
1291         
1292         Roo.log('button on click ');
1293         if(this.preventDefault){
1294             e.preventDefault();
1295         }
1296         
1297         if (this.pressed === true || this.pressed === false) {
1298             this.toggleActive(e);
1299         }
1300         
1301         
1302         this.fireEvent('click', this, e);
1303     },
1304     
1305     /**
1306      * Enables this button
1307      */
1308     enable : function()
1309     {
1310         this.disabled = false;
1311         this.el.removeClass('disabled');
1312     },
1313     
1314     /**
1315      * Disable this button
1316      */
1317     disable : function()
1318     {
1319         this.disabled = true;
1320         this.el.addClass('disabled');
1321     },
1322      /**
1323      * sets the active state on/off, 
1324      * @param {Boolean} state (optional) Force a particular state
1325      */
1326     setActive : function(v) {
1327         
1328         this.el[v ? 'addClass' : 'removeClass']('active');
1329         this.pressed = v;
1330     },
1331      /**
1332      * toggles the current active state 
1333      */
1334     toggleActive : function(e)
1335     {
1336         this.setActive(!this.pressed);
1337         this.fireEvent('toggle', this, e, !this.pressed);
1338     },
1339      /**
1340      * get the current active state
1341      * @return {boolean} true if it's active
1342      */
1343     isActive : function()
1344     {
1345         return this.el.hasClass('active');
1346     },
1347     /**
1348      * set the text of the first selected button
1349      */
1350     setText : function(str)
1351     {
1352         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1353     },
1354     /**
1355      * get the text of the first selected button
1356      */
1357     getText : function()
1358     {
1359         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1360     },
1361     
1362     setWeight : function(str)
1363     {
1364         this.el.removeClass(this.weightClass);
1365         this.weight = str;
1366         var outline = this.outline ? 'outline-' : '';
1367         if (str == 'default') {
1368             this.el.addClass('btn-default btn-outline-secondary');        
1369             return;
1370         }
1371         this.el.addClass('btn-' + outline + str);        
1372     }
1373     
1374     
1375 });
1376
1377  /*
1378  * - LGPL
1379  *
1380  * column
1381  * 
1382  */
1383
1384 /**
1385  * @class Roo.bootstrap.Column
1386  * @extends Roo.bootstrap.Component
1387  * Bootstrap Column class
1388  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1389  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1390  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1391  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1392  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1393  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1394  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1395  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1396  *
1397  * 
1398  * @cfg {Boolean} hidden (true|false) hide the element
1399  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1400  * @cfg {String} fa (ban|check|...) font awesome icon
1401  * @cfg {Number} fasize (1|2|....) font awsome size
1402
1403  * @cfg {String} icon (info-sign|check|...) glyphicon name
1404
1405  * @cfg {String} html content of column.
1406  * 
1407  * @constructor
1408  * Create a new Column
1409  * @param {Object} config The config object
1410  */
1411
1412 Roo.bootstrap.Column = function(config){
1413     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1414 };
1415
1416 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1417     
1418     xs: false,
1419     sm: false,
1420     md: false,
1421     lg: false,
1422     xsoff: false,
1423     smoff: false,
1424     mdoff: false,
1425     lgoff: false,
1426     html: '',
1427     offset: 0,
1428     alert: false,
1429     fa: false,
1430     icon : false,
1431     hidden : false,
1432     fasize : 1,
1433     
1434     getAutoCreate : function(){
1435         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1436         
1437         cfg = {
1438             tag: 'div',
1439             cls: 'column'
1440         };
1441         
1442         var settings=this;
1443         var sizes =   ['xs','sm','md','lg'];
1444         sizes.map(function(size ,ix){
1445             //Roo.log( size + ':' + settings[size]);
1446             
1447             if (settings[size+'off'] !== false) {
1448                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1449             }
1450             
1451             if (settings[size] === false) {
1452                 return;
1453             }
1454             
1455             if (!settings[size]) { // 0 = hidden
1456                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1457                 // bootsrap4
1458                 for (var i = ix; i > -1; i--) {
1459                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1460                 }
1461                 
1462                 
1463                 return;
1464             }
1465             cfg.cls += ' col-' + size + '-' + settings[size] + (
1466                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1467             );
1468             
1469         });
1470         
1471         if (this.hidden) {
1472             cfg.cls += ' hidden';
1473         }
1474         
1475         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1476             cfg.cls +=' alert alert-' + this.alert;
1477         }
1478         
1479         
1480         if (this.html.length) {
1481             cfg.html = this.html;
1482         }
1483         if (this.fa) {
1484             var fasize = '';
1485             if (this.fasize > 1) {
1486                 fasize = ' fa-' + this.fasize + 'x';
1487             }
1488             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1489             
1490             
1491         }
1492         if (this.icon) {
1493             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1494         }
1495         
1496         return cfg;
1497     }
1498    
1499 });
1500
1501  
1502
1503  /*
1504  * - LGPL
1505  *
1506  * page container.
1507  * 
1508  */
1509
1510
1511 /**
1512  * @class Roo.bootstrap.Container
1513  * @extends Roo.bootstrap.Component
1514  * Bootstrap Container class
1515  * @cfg {Boolean} jumbotron is it a jumbotron element
1516  * @cfg {String} html content of element
1517  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1518  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1519  * @cfg {String} header content of header (for panel)
1520  * @cfg {String} footer content of footer (for panel)
1521  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1522  * @cfg {String} tag (header|aside|section) type of HTML tag.
1523  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1524  * @cfg {String} fa font awesome icon
1525  * @cfg {String} icon (info-sign|check|...) glyphicon name
1526  * @cfg {Boolean} hidden (true|false) hide the element
1527  * @cfg {Boolean} expandable (true|false) default false
1528  * @cfg {Boolean} expanded (true|false) default true
1529  * @cfg {String} rheader contet on the right of header
1530  * @cfg {Boolean} clickable (true|false) default false
1531
1532  *     
1533  * @constructor
1534  * Create a new Container
1535  * @param {Object} config The config object
1536  */
1537
1538 Roo.bootstrap.Container = function(config){
1539     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1540     
1541     this.addEvents({
1542         // raw events
1543          /**
1544          * @event expand
1545          * After the panel has been expand
1546          * 
1547          * @param {Roo.bootstrap.Container} this
1548          */
1549         "expand" : true,
1550         /**
1551          * @event collapse
1552          * After the panel has been collapsed
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "collapse" : true,
1557         /**
1558          * @event click
1559          * When a element is chick
1560          * @param {Roo.bootstrap.Container} this
1561          * @param {Roo.EventObject} e
1562          */
1563         "click" : true
1564     });
1565 };
1566
1567 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1568     
1569     jumbotron : false,
1570     well: '',
1571     panel : '',
1572     header: '',
1573     footer : '',
1574     sticky: '',
1575     tag : false,
1576     alert : false,
1577     fa: false,
1578     icon : false,
1579     expandable : false,
1580     rheader : '',
1581     expanded : true,
1582     clickable: false,
1583   
1584      
1585     getChildContainer : function() {
1586         
1587         if(!this.el){
1588             return false;
1589         }
1590         
1591         if (this.panel.length) {
1592             return this.el.select('.panel-body',true).first();
1593         }
1594         
1595         return this.el;
1596     },
1597     
1598     
1599     getAutoCreate : function(){
1600         
1601         var cfg = {
1602             tag : this.tag || 'div',
1603             html : '',
1604             cls : ''
1605         };
1606         if (this.jumbotron) {
1607             cfg.cls = 'jumbotron';
1608         }
1609         
1610         
1611         
1612         // - this is applied by the parent..
1613         //if (this.cls) {
1614         //    cfg.cls = this.cls + '';
1615         //}
1616         
1617         if (this.sticky.length) {
1618             
1619             var bd = Roo.get(document.body);
1620             if (!bd.hasClass('bootstrap-sticky')) {
1621                 bd.addClass('bootstrap-sticky');
1622                 Roo.select('html',true).setStyle('height', '100%');
1623             }
1624              
1625             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1626         }
1627         
1628         
1629         if (this.well.length) {
1630             switch (this.well) {
1631                 case 'lg':
1632                 case 'sm':
1633                     cfg.cls +=' well well-' +this.well;
1634                     break;
1635                 default:
1636                     cfg.cls +=' well';
1637                     break;
1638             }
1639         }
1640         
1641         if (this.hidden) {
1642             cfg.cls += ' hidden';
1643         }
1644         
1645         
1646         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1647             cfg.cls +=' alert alert-' + this.alert;
1648         }
1649         
1650         var body = cfg;
1651         
1652         if (this.panel.length) {
1653             cfg.cls += ' panel panel-' + this.panel;
1654             cfg.cn = [];
1655             if (this.header.length) {
1656                 
1657                 var h = [];
1658                 
1659                 if(this.expandable){
1660                     
1661                     cfg.cls = cfg.cls + ' expandable';
1662                     
1663                     h.push({
1664                         tag: 'i',
1665                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1666                     });
1667                     
1668                 }
1669                 
1670                 h.push(
1671                     {
1672                         tag: 'span',
1673                         cls : 'panel-title',
1674                         html : (this.expandable ? '&nbsp;' : '') + this.header
1675                     },
1676                     {
1677                         tag: 'span',
1678                         cls: 'panel-header-right',
1679                         html: this.rheader
1680                     }
1681                 );
1682                 
1683                 cfg.cn.push({
1684                     cls : 'panel-heading',
1685                     style : this.expandable ? 'cursor: pointer' : '',
1686                     cn : h
1687                 });
1688                 
1689             }
1690             
1691             body = false;
1692             cfg.cn.push({
1693                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1694                 html : this.html
1695             });
1696             
1697             
1698             if (this.footer.length) {
1699                 cfg.cn.push({
1700                     cls : 'panel-footer',
1701                     html : this.footer
1702                     
1703                 });
1704             }
1705             
1706         }
1707         
1708         if (body) {
1709             body.html = this.html || cfg.html;
1710             // prefix with the icons..
1711             if (this.fa) {
1712                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1713             }
1714             if (this.icon) {
1715                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1716             }
1717             
1718             
1719         }
1720         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1721             cfg.cls =  'container';
1722         }
1723         
1724         return cfg;
1725     },
1726     
1727     initEvents: function() 
1728     {
1729         if(this.expandable){
1730             var headerEl = this.headerEl();
1731         
1732             if(headerEl){
1733                 headerEl.on('click', this.onToggleClick, this);
1734             }
1735         }
1736         
1737         if(this.clickable){
1738             this.el.on('click', this.onClick, this);
1739         }
1740         
1741     },
1742     
1743     onToggleClick : function()
1744     {
1745         var headerEl = this.headerEl();
1746         
1747         if(!headerEl){
1748             return;
1749         }
1750         
1751         if(this.expanded){
1752             this.collapse();
1753             return;
1754         }
1755         
1756         this.expand();
1757     },
1758     
1759     expand : function()
1760     {
1761         if(this.fireEvent('expand', this)) {
1762             
1763             this.expanded = true;
1764             
1765             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1766             
1767             this.el.select('.panel-body',true).first().removeClass('hide');
1768             
1769             var toggleEl = this.toggleEl();
1770
1771             if(!toggleEl){
1772                 return;
1773             }
1774
1775             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1776         }
1777         
1778     },
1779     
1780     collapse : function()
1781     {
1782         if(this.fireEvent('collapse', this)) {
1783             
1784             this.expanded = false;
1785             
1786             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1787             this.el.select('.panel-body',true).first().addClass('hide');
1788         
1789             var toggleEl = this.toggleEl();
1790
1791             if(!toggleEl){
1792                 return;
1793             }
1794
1795             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1796         }
1797     },
1798     
1799     toggleEl : function()
1800     {
1801         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1802             return;
1803         }
1804         
1805         return this.el.select('.panel-heading .fa',true).first();
1806     },
1807     
1808     headerEl : function()
1809     {
1810         if(!this.el || !this.panel.length || !this.header.length){
1811             return;
1812         }
1813         
1814         return this.el.select('.panel-heading',true).first()
1815     },
1816     
1817     bodyEl : function()
1818     {
1819         if(!this.el || !this.panel.length){
1820             return;
1821         }
1822         
1823         return this.el.select('.panel-body',true).first()
1824     },
1825     
1826     titleEl : function()
1827     {
1828         if(!this.el || !this.panel.length || !this.header.length){
1829             return;
1830         }
1831         
1832         return this.el.select('.panel-title',true).first();
1833     },
1834     
1835     setTitle : function(v)
1836     {
1837         var titleEl = this.titleEl();
1838         
1839         if(!titleEl){
1840             return;
1841         }
1842         
1843         titleEl.dom.innerHTML = v;
1844     },
1845     
1846     getTitle : function()
1847     {
1848         
1849         var titleEl = this.titleEl();
1850         
1851         if(!titleEl){
1852             return '';
1853         }
1854         
1855         return titleEl.dom.innerHTML;
1856     },
1857     
1858     setRightTitle : function(v)
1859     {
1860         var t = this.el.select('.panel-header-right',true).first();
1861         
1862         if(!t){
1863             return;
1864         }
1865         
1866         t.dom.innerHTML = v;
1867     },
1868     
1869     onClick : function(e)
1870     {
1871         e.preventDefault();
1872         
1873         this.fireEvent('click', this, e);
1874     }
1875 });
1876
1877  /*
1878  *  - LGPL
1879  *
1880  *  This is BS4's Card element.. - similar to our containers probably..
1881  * 
1882  */
1883 /**
1884  * @class Roo.bootstrap.Card
1885  * @extends Roo.bootstrap.Component
1886  * Bootstrap Card class
1887  *
1888  *
1889  * possible... may not be implemented..
1890  * @cfg {String} header_image  src url of image.
1891  * @cfg {String|Object} header
1892  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1893  * 
1894  * @cfg {String} title
1895  * @cfg {String} subtitle
1896  * @cfg {String} html -- html contents - or just use children..
1897  * @cfg {String} footer
1898  
1899  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1900  * 
1901  * @cfg {String} margin (0|1|2|3|4|5|auto)
1902  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1903  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1904  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1905  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1906  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1907  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1908  *
1909  * @cfg {String} padding (0|1|2|3|4|5)
1910  * @cfg {String} padding_top (0|1|2|3|4|5)
1911  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1912  * @cfg {String} padding_left (0|1|2|3|4|5)
1913  * @cfg {String} padding_right (0|1|2|3|4|5)
1914  * @cfg {String} padding_x (0|1|2|3|4|5)
1915  * @cfg {String} padding_y (0|1|2|3|4|5)
1916  *
1917  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1918  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1919  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1920  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1921  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1922  
1923  * @config {Boolean} dragable  if this card can be dragged.
1924  * @config {String} drag_group  group for drag
1925  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1926  * @config {String} drop_group  group for drag
1927  * 
1928  * @config {Boolean} collapsable can the body be collapsed.
1929  * @config {Boolean} collapsed is the body collapsed when rendered...
1930  * @constructor
1931  * Create a new Container
1932  * @param {Object} config The config object
1933  */
1934
1935 Roo.bootstrap.Card = function(config){
1936     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1937     
1938     this.addEvents({
1939          // raw events
1940         /**
1941          * @event drop
1942          * When a element a card is dropped
1943          * @param {Roo.bootstrap.Element} this
1944          * @param {Roo.Element} n the node being dropped?
1945          * @param {Object} dd Drag and drop data
1946          * @param {Roo.EventObject} e
1947          * @param {Roo.EventObject} data  the data passed via getDragData
1948          */
1949         'drop' : true
1950         
1951     });
1952 };
1953
1954
1955 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1956     
1957     
1958     weight : '',
1959     
1960     margin: '', /// may be better in component?
1961     margin_top: '', 
1962     margin_bottom: '', 
1963     margin_left: '',
1964     margin_right: '',
1965     margin_x: '',
1966     margin_y: '',
1967     
1968     padding : '',
1969     padding_top: '', 
1970     padding_bottom: '', 
1971     padding_left: '',
1972     padding_right: '',
1973     padding_x: '',
1974     padding_y: '',
1975     
1976     display: '', 
1977     display_xs: '', 
1978     display_sm: '', 
1979     display_lg: '',
1980     display_xl: '',
1981  
1982     header_image  : '',
1983     header : '',
1984     header_size : 0,
1985     title : '',
1986     subtitle : '',
1987     html : '',
1988     footer: '',
1989
1990     collapsable : false,
1991     collapsed : false,
1992     
1993     dragable : false,
1994     drag_group : false,
1995     dropable : false,
1996     drop_group : false,
1997     childContainer : false,
1998     dropEl : false, /// the dom placeholde element that indicates drop location.
1999     
2000     layoutCls : function()
2001     {
2002         var cls = '';
2003         var t = this;
2004         Roo.log(this.margin_bottom.length);
2005         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2006             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2007             
2008             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2009                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2010             }
2011             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2012                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2013             }
2014         });
2015         
2016         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2017             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2018                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2019             }
2020         });
2021         
2022         // more generic support?
2023         if (this.hidden) {
2024             cls += ' d-none';
2025         }
2026         
2027         return cls;
2028     },
2029  
2030        // Roo.log("Call onRender: " + this.xtype);
2031         /*  We are looking at something like this.
2032 <div class="card">
2033     <img src="..." class="card-img-top" alt="...">
2034     <div class="card-body">
2035         <h5 class="card-title">Card title</h5>
2036          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2037
2038         >> this bit is really the body...
2039         <div> << we will ad dthis in hopefully it will not break shit.
2040         
2041         ** card text does not actually have any styling...
2042         
2043             <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>
2044         
2045         </div> <<
2046           <a href="#" class="card-link">Card link</a>
2047           
2048     </div>
2049     <div class="card-footer">
2050         <small class="text-muted">Last updated 3 mins ago</small>
2051     </div>
2052 </div>
2053          */
2054     getAutoCreate : function(){
2055         
2056         var cfg = {
2057             tag : 'div',
2058             cls : 'card',
2059             cn : [ ]
2060         };
2061         
2062         if (this.weight.length && this.weight != 'light') {
2063             cfg.cls += ' text-white';
2064         } else {
2065             cfg.cls += ' text-dark'; // need as it's nested..
2066         }
2067         if (this.weight.length) {
2068             cfg.cls += ' bg-' + this.weight;
2069         }
2070         
2071         cfg.cls += this.layoutCls(); 
2072         
2073     var hdr = false;
2074         if (this.header.length) {
2075             hdr = {
2076                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2077                 cls : 'card-header',
2078         cn : []
2079             };
2080         cfg.cn.push(hdr);
2081         hdr_ctr = hdr;
2082         } else {
2083         hdr = {
2084                 tag : 'div',
2085                 cls : 'card-header d-none',
2086         cn : []
2087             };
2088         cfg.cn.push(hdr);
2089     }
2090     if (this.collapsable) {
2091         hdr_ctr = {
2092         tag : 'a',
2093         cls : 'd-block user-select-none',
2094         cn: [
2095             {
2096             tag: 'i',
2097             cls : 'roo-collapse-toggle fa fa-chevron-down float-right'
2098             }
2099            
2100         ]
2101         };
2102         hdr.cn.push(hdr_ctr);
2103     }
2104     if (this.header.length) {
2105         hdr_ctr.cn.push(        {
2106         tag: 'span',
2107         cls: 'roo-card-header-ctr',
2108         html : this.header
2109         })
2110     }
2111     
2112         if (this.header_image.length) {
2113             cfg.cn.push({
2114                 tag : 'img',
2115                 cls : 'card-img-top',
2116                 src: this.header_image // escape?
2117             });
2118         } else {
2119         cfg.cn.push({
2120                 tag : 'div',
2121                 cls : 'card-img-top d-none' 
2122             });
2123     }
2124         
2125         var body = {
2126             tag : 'div',
2127             cls : 'card-body',
2128             cn : []
2129         };
2130     var obody = body;
2131     if (this.collapsable) {
2132         obody = {
2133         tag: 'div',
2134         cls : 'roo-collapsable collapse ' + (this.collapsed ? '' : 'show'),
2135         cn : [  body ]
2136         };
2137     }
2138     
2139         cfg.cn.push(obody);
2140         
2141         if (this.title.length) {
2142             body.cn.push({
2143                 tag : 'div',
2144                 cls : 'card-title',
2145                 src: this.title // escape?
2146             });
2147         }
2148         
2149         if (this.subtitle.length) {
2150             body.cn.push({
2151                 tag : 'div',
2152                 cls : 'card-title',
2153                 src: this.subtitle // escape?
2154             });
2155         }
2156         
2157         body.cn.push({
2158             tag : 'div',
2159             cls : 'roo-card-body-ctr'
2160         });
2161         
2162         if (this.html.length) {
2163             body.cn.push({
2164                 tag: 'div',
2165                 html : this.html
2166             });
2167         }
2168         // fixme ? handle objects?
2169         if (this.footer.length) {
2170             cfg.cn.push({
2171                 tag : 'div',
2172                 cls : 'card-footer',
2173                 html: this.footer // escape?
2174             });
2175         }
2176         // footer...
2177         
2178         return cfg;
2179     },
2180     
2181     
2182     getCardHeader : function()
2183     {
2184         var  ret = this.el.select('.card-header',true).first();
2185     if (ret.hasClass('d-none')) {
2186         ret.removeClass('d-none');
2187     }
2188         
2189         return ret;
2190     },
2191     
2192     getCardImageTop : function()
2193     {
2194         var  ret = this.el.select('.card-img-top',true).first();
2195     if (ret.hasClass('d-none')) {
2196         ret.removeClass('d-none');
2197     }
2198         
2199         return ret;
2200     },
2201     
2202     getChildContainer : function()
2203     {
2204         
2205         if(!this.el){
2206             return false;
2207         }
2208         return this.el.select('.roo-card-body-ctr',true).first();    
2209     },
2210     
2211     initEvents: function() 
2212     {
2213         
2214     this.bodyEl = this.getChildContainer();
2215     if(this.dragable){
2216             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2217                     containerScroll: true,
2218                     ddGroup: this.drag_group || 'default_card_drag_group'
2219             });
2220             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2221         }
2222         if (this.dropable) {
2223         this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2224             containerScroll: true,
2225             ddGroup: this.drop_group || 'default_card_drag_group'
2226         });
2227         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2228         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2229         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2230         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2231         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2232     }
2233         
2234         if (this.collapsable) {
2235         this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2236     }
2237     },
2238     getDragData : function(e)
2239     {
2240         var target = this.getEl();
2241         if (target) {
2242             //this.handleSelection(e);
2243             
2244             var dragData = {
2245                 source: this,
2246                 copy: false,
2247                 nodes: this.getEl(),
2248                 records: []
2249             };
2250             
2251             
2252             dragData.ddel = target.dom ;    // the div element
2253             Roo.log(target.getWidth( ));
2254             dragData.ddel.style.width = target.getWidth() + 'px';
2255             
2256             return dragData;
2257         }
2258         return false;
2259     },
2260     /**
2261  *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2262  *    whole Element becomes the target, and this causes the drop gesture to append.
2263  */
2264     getTargetFromEvent : function(e, dragged_card_el)
2265     {
2266         var target = e.getTarget();
2267         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2268             target = target.parentNode;
2269         }
2270         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2271         // see if target is one of the 'cards'...
2272         var ctarget = -1;
2273         var cards = [];
2274         //Roo.log(this.items.length);
2275         var lpos = pos = cpos = false;
2276         for (var i = 0;i< this.items.length;i++) {
2277             
2278             if (!this.items[i].el.hasClass('card')) {
2279                  continue;
2280             }
2281             pos = this.getDropPoint(e, this.items[i].el.dom);
2282             
2283             //Roo.log(this.items[i].el.dom.id);
2284             var ii = cards.length;
2285             cards.push(this.items[i]);
2286             
2287             if (ctarget < 0 && pos == 'above') {
2288                 ctarget = ii > 0 ? ii - 1 : 0;
2289                 cpos = ii > 0 ? 'below' : pos;
2290             }
2291         }
2292         if (!cards.length) {
2293             return [ true, 'below' ];
2294         }
2295         
2296         if (ctarget < 0) {
2297             ctarget = cards.length -1;
2298             cpos = 'below';
2299         }
2300         if (cards[ctarget].el == dragged_card_el) {
2301             return false;
2302         }
2303         
2304         if (cpos == 'below') {
2305             var card_after = ctarget+1 == cards.length ? false : cards[ctarget+1];
2306             
2307             // then above should not be dragged_card_el.
2308             // and ctarget sho
2309             
2310             if (card_after  && card_after.el == dragged_card_el) {
2311                 return false;
2312             }
2313             return [ cards[ctarget], cpos ];
2314         }
2315         
2316         // its's after ..
2317         var card_before = ctarget > 0 ? cards[ctarget-1] : false;
2318         
2319             
2320         if (card_before  && card_before.el == dragged_card_el) {
2321             return false;
2322         }
2323         
2324         return [ cards[ctarget], cpos, cards, ctarget ];
2325     },
2326     
2327     onNodeEnter : function(n, dd, e, data){
2328         return false;
2329     },
2330     onNodeOver : function(n, dd, e, data)
2331     {
2332        
2333         var target_info = this.getTargetFromEvent(e,data.source.el);
2334         if (target_info === false) {
2335             this.dropPlaceHolder('hide');
2336             return false;
2337         }
2338         Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2339         
2340          
2341         this.dropPlaceHolder('show', target_info,data);
2342         
2343         return false; 
2344     },
2345     onNodeOut : function(n, dd, e, data){
2346         this.dropPlaceHolder('hide');
2347      
2348     },
2349     onNodeDrop : function(n, dd, e, data)
2350     {
2351         
2352         // call drop - return false if  
2353         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2354             return false;
2355         }
2356         
2357         var target_info = this.getTargetFromEvent(e,data.source.el);
2358         if (target_info === false) {
2359             return false;
2360         }
2361         
2362         var pt = this.getDropPoint(e, n, dd);
2363         var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2364         if (pt == "below") {
2365             insertAt++;
2366         }
2367         for (var i = 0; i < this.items.length; i++) {
2368             var r = this.items[i];
2369             //var dup = this.store.getById(r.id);
2370             if (dup && (dd != this.dragZone)) {
2371                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2372             } else {
2373             if (data.copy) {
2374                 this.store.insert(insertAt++, r.copy());
2375             } else {
2376                 data.source.isDirtyFlag = true;
2377                 r.store.remove(r);
2378                 this.store.insert(insertAt++, r);
2379             }
2380             this.isDirtyFlag = true;
2381             }
2382         }
2383         this.dragZone.cachedTarget = null;
2384         return true;
2385     },
2386     
2387     /**    Decide whether to drop above or below a View node. */
2388     getDropPoint : function(e, n, dd)
2389     {
2390         if (dd) {
2391              return false;
2392         }
2393         if (n == this.bodyEl.dom) {
2394             return "above";
2395         }
2396         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2397         var c = t + (b - t) / 2;
2398         var y = Roo.lib.Event.getPageY(e);
2399         if(y <= c) {
2400             return "above";
2401         }else{
2402             return "below";
2403         }
2404     },
2405     onToggleCollapse : function(e)
2406         {
2407         if (this.collapsed) {
2408             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2409             this.el.select('.roo-collapsable').addClass('show');
2410             this.collapsed = false;
2411             return;
2412         }
2413         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2414         this.el.select('.roo-collapsable').removeClass('show');
2415         this.collapsed = true;
2416         
2417     
2418     },
2419     dropPlaceHolder: function (action, where_ar, data)
2420     {
2421         if (this.dropEl === false) {
2422             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2423             cls : 'd-none'
2424             },true);
2425         }
2426         this.dropEl.removeClass(['d-none', 'd-block']);        
2427         if (action == 'hide') {
2428             
2429             this.dropEl.addClass('d-none');
2430             return;
2431         }
2432         var cardel = where_ar[0].el.dom;
2433         
2434         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2435         if (where_ar[1] == 'above') {
2436             cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2437         } else if (cardel.nextSibling) {
2438             cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2439         } else {
2440             cardel.parentNode.append(this.dropEl.dom);
2441         }
2442         this.dropEl.addClass('d-block roo-card-dropzone');
2443         
2444         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2445         
2446         
2447     
2448     
2449     
2450     }
2451
2452     
2453 });
2454
2455 /*
2456  * - LGPL
2457  *
2458  * Card header - holder for the card header elements.
2459  * 
2460  */
2461
2462 /**
2463  * @class Roo.bootstrap.CardHeader
2464  * @extends Roo.bootstrap.Element
2465  * Bootstrap CardHeader class
2466  * @constructor
2467  * Create a new Card Header - that you can embed children into
2468  * @param {Object} config The config object
2469  */
2470
2471 Roo.bootstrap.CardHeader = function(config){
2472     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2473 };
2474
2475 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2476     
2477     
2478     container_method : 'getCardHeader' 
2479     
2480      
2481     
2482     
2483    
2484 });
2485
2486  
2487
2488  /*
2489  * - LGPL
2490  *
2491  * Card header - holder for the card header elements.
2492  * 
2493  */
2494
2495 /**
2496  * @class Roo.bootstrap.CardImageTop
2497  * @extends Roo.bootstrap.Element
2498  * Bootstrap CardImageTop class
2499  * @constructor
2500  * Create a new Card Image Top container
2501  * @param {Object} config The config object
2502  */
2503
2504 Roo.bootstrap.CardImageTop = function(config){
2505     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2506 };
2507
2508 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2509     
2510    
2511     container_method : 'getCardImageTop' 
2512     
2513      
2514     
2515    
2516 });
2517
2518  
2519
2520  /*
2521  * - LGPL
2522  *
2523  * image
2524  * 
2525  */
2526
2527
2528 /**
2529  * @class Roo.bootstrap.Img
2530  * @extends Roo.bootstrap.Component
2531  * Bootstrap Img class
2532  * @cfg {Boolean} imgResponsive false | true
2533  * @cfg {String} border rounded | circle | thumbnail
2534  * @cfg {String} src image source
2535  * @cfg {String} alt image alternative text
2536  * @cfg {String} href a tag href
2537  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2538  * @cfg {String} xsUrl xs image source
2539  * @cfg {String} smUrl sm image source
2540  * @cfg {String} mdUrl md image source
2541  * @cfg {String} lgUrl lg image source
2542  * 
2543  * @constructor
2544  * Create a new Input
2545  * @param {Object} config The config object
2546  */
2547
2548 Roo.bootstrap.Img = function(config){
2549     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2550     
2551     this.addEvents({
2552         // img events
2553         /**
2554          * @event click
2555          * The img click event for the img.
2556          * @param {Roo.EventObject} e
2557          */
2558         "click" : true
2559     });
2560 };
2561
2562 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2563     
2564     imgResponsive: true,
2565     border: '',
2566     src: 'about:blank',
2567     href: false,
2568     target: false,
2569     xsUrl: '',
2570     smUrl: '',
2571     mdUrl: '',
2572     lgUrl: '',
2573
2574     getAutoCreate : function()
2575     {   
2576         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2577             return this.createSingleImg();
2578         }
2579         
2580         var cfg = {
2581             tag: 'div',
2582             cls: 'roo-image-responsive-group',
2583             cn: []
2584         };
2585         var _this = this;
2586         
2587         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2588             
2589             if(!_this[size + 'Url']){
2590                 return;
2591             }
2592             
2593             var img = {
2594                 tag: 'img',
2595                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2596                 html: _this.html || cfg.html,
2597                 src: _this[size + 'Url']
2598             };
2599             
2600             img.cls += ' roo-image-responsive-' + size;
2601             
2602             var s = ['xs', 'sm', 'md', 'lg'];
2603             
2604             s.splice(s.indexOf(size), 1);
2605             
2606             Roo.each(s, function(ss){
2607                 img.cls += ' hidden-' + ss;
2608             });
2609             
2610             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2611                 cfg.cls += ' img-' + _this.border;
2612             }
2613             
2614             if(_this.alt){
2615                 cfg.alt = _this.alt;
2616             }
2617             
2618             if(_this.href){
2619                 var a = {
2620                     tag: 'a',
2621                     href: _this.href,
2622                     cn: [
2623                         img
2624                     ]
2625                 };
2626
2627                 if(this.target){
2628                     a.target = _this.target;
2629                 }
2630             }
2631             
2632             cfg.cn.push((_this.href) ? a : img);
2633             
2634         });
2635         
2636         return cfg;
2637     },
2638     
2639     createSingleImg : function()
2640     {
2641         var cfg = {
2642             tag: 'img',
2643             cls: (this.imgResponsive) ? 'img-responsive' : '',
2644             html : null,
2645             src : 'about:blank'  // just incase src get's set to undefined?!?
2646         };
2647         
2648         cfg.html = this.html || cfg.html;
2649         
2650         cfg.src = this.src || cfg.src;
2651         
2652         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2653             cfg.cls += ' img-' + this.border;
2654         }
2655         
2656         if(this.alt){
2657             cfg.alt = this.alt;
2658         }
2659         
2660         if(this.href){
2661             var a = {
2662                 tag: 'a',
2663                 href: this.href,
2664                 cn: [
2665                     cfg
2666                 ]
2667             };
2668             
2669             if(this.target){
2670                 a.target = this.target;
2671             }
2672             
2673         }
2674         
2675         return (this.href) ? a : cfg;
2676     },
2677     
2678     initEvents: function() 
2679     {
2680         if(!this.href){
2681             this.el.on('click', this.onClick, this);
2682         }
2683         
2684     },
2685     
2686     onClick : function(e)
2687     {
2688         Roo.log('img onclick');
2689         this.fireEvent('click', this, e);
2690     },
2691     /**
2692      * Sets the url of the image - used to update it
2693      * @param {String} url the url of the image
2694      */
2695     
2696     setSrc : function(url)
2697     {
2698         this.src =  url;
2699         
2700         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2701             this.el.dom.src =  url;
2702             return;
2703         }
2704         
2705         this.el.select('img', true).first().dom.src =  url;
2706     }
2707     
2708     
2709    
2710 });
2711
2712  /*
2713  * - LGPL
2714  *
2715  * image
2716  * 
2717  */
2718
2719
2720 /**
2721  * @class Roo.bootstrap.Link
2722  * @extends Roo.bootstrap.Component
2723  * Bootstrap Link Class
2724  * @cfg {String} alt image alternative text
2725  * @cfg {String} href a tag href
2726  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2727  * @cfg {String} html the content of the link.
2728  * @cfg {String} anchor name for the anchor link
2729  * @cfg {String} fa - favicon
2730
2731  * @cfg {Boolean} preventDefault (true | false) default false
2732
2733  * 
2734  * @constructor
2735  * Create a new Input
2736  * @param {Object} config The config object
2737  */
2738
2739 Roo.bootstrap.Link = function(config){
2740     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2741     
2742     this.addEvents({
2743         // img events
2744         /**
2745          * @event click
2746          * The img click event for the img.
2747          * @param {Roo.EventObject} e
2748          */
2749         "click" : true
2750     });
2751 };
2752
2753 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2754     
2755     href: false,
2756     target: false,
2757     preventDefault: false,
2758     anchor : false,
2759     alt : false,
2760     fa: false,
2761
2762
2763     getAutoCreate : function()
2764     {
2765         var html = this.html || '';
2766         
2767         if (this.fa !== false) {
2768             html = '<i class="fa fa-' + this.fa + '"></i>';
2769         }
2770         var cfg = {
2771             tag: 'a'
2772         };
2773         // anchor's do not require html/href...
2774         if (this.anchor === false) {
2775             cfg.html = html;
2776             cfg.href = this.href || '#';
2777         } else {
2778             cfg.name = this.anchor;
2779             if (this.html !== false || this.fa !== false) {
2780                 cfg.html = html;
2781             }
2782             if (this.href !== false) {
2783                 cfg.href = this.href;
2784             }
2785         }
2786         
2787         if(this.alt !== false){
2788             cfg.alt = this.alt;
2789         }
2790         
2791         
2792         if(this.target !== false) {
2793             cfg.target = this.target;
2794         }
2795         
2796         return cfg;
2797     },
2798     
2799     initEvents: function() {
2800         
2801         if(!this.href || this.preventDefault){
2802             this.el.on('click', this.onClick, this);
2803         }
2804     },
2805     
2806     onClick : function(e)
2807     {
2808         if(this.preventDefault){
2809             e.preventDefault();
2810         }
2811         //Roo.log('img onclick');
2812         this.fireEvent('click', this, e);
2813     }
2814    
2815 });
2816
2817  /*
2818  * - LGPL
2819  *
2820  * header
2821  * 
2822  */
2823
2824 /**
2825  * @class Roo.bootstrap.Header
2826  * @extends Roo.bootstrap.Component
2827  * Bootstrap Header class
2828  * @cfg {String} html content of header
2829  * @cfg {Number} level (1|2|3|4|5|6) default 1
2830  * 
2831  * @constructor
2832  * Create a new Header
2833  * @param {Object} config The config object
2834  */
2835
2836
2837 Roo.bootstrap.Header  = function(config){
2838     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2839 };
2840
2841 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2842     
2843     //href : false,
2844     html : false,
2845     level : 1,
2846     
2847     
2848     
2849     getAutoCreate : function(){
2850         
2851         
2852         
2853         var cfg = {
2854             tag: 'h' + (1 *this.level),
2855             html: this.html || ''
2856         } ;
2857         
2858         return cfg;
2859     }
2860    
2861 });
2862
2863  
2864
2865  /*
2866  * Based on:
2867  * Ext JS Library 1.1.1
2868  * Copyright(c) 2006-2007, Ext JS, LLC.
2869  *
2870  * Originally Released Under LGPL - original licence link has changed is not relivant.
2871  *
2872  * Fork - LGPL
2873  * <script type="text/javascript">
2874  */
2875  
2876 /**
2877  * @class Roo.bootstrap.MenuMgr
2878  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2879  * @singleton
2880  */
2881 Roo.bootstrap.MenuMgr = function(){
2882    var menus, active, groups = {}, attached = false, lastShow = new Date();
2883
2884    // private - called when first menu is created
2885    function init(){
2886        menus = {};
2887        active = new Roo.util.MixedCollection();
2888        Roo.get(document).addKeyListener(27, function(){
2889            if(active.length > 0){
2890                hideAll();
2891            }
2892        });
2893    }
2894
2895    // private
2896    function hideAll(){
2897        if(active && active.length > 0){
2898            var c = active.clone();
2899            c.each(function(m){
2900                m.hide();
2901            });
2902        }
2903    }
2904
2905    // private
2906    function onHide(m){
2907        active.remove(m);
2908        if(active.length < 1){
2909            Roo.get(document).un("mouseup", onMouseDown);
2910             
2911            attached = false;
2912        }
2913    }
2914
2915    // private
2916    function onShow(m){
2917        var last = active.last();
2918        lastShow = new Date();
2919        active.add(m);
2920        if(!attached){
2921           Roo.get(document).on("mouseup", onMouseDown);
2922            
2923            attached = true;
2924        }
2925        if(m.parentMenu){
2926           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2927           m.parentMenu.activeChild = m;
2928        }else if(last && last.isVisible()){
2929           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2930        }
2931    }
2932
2933    // private
2934    function onBeforeHide(m){
2935        if(m.activeChild){
2936            m.activeChild.hide();
2937        }
2938        if(m.autoHideTimer){
2939            clearTimeout(m.autoHideTimer);
2940            delete m.autoHideTimer;
2941        }
2942    }
2943
2944    // private
2945    function onBeforeShow(m){
2946        var pm = m.parentMenu;
2947        if(!pm && !m.allowOtherMenus){
2948            hideAll();
2949        }else if(pm && pm.activeChild && active != m){
2950            pm.activeChild.hide();
2951        }
2952    }
2953
2954    // private this should really trigger on mouseup..
2955    function onMouseDown(e){
2956         Roo.log("on Mouse Up");
2957         
2958         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2959             Roo.log("MenuManager hideAll");
2960             hideAll();
2961             e.stopEvent();
2962         }
2963         
2964         
2965    }
2966
2967    // private
2968    function onBeforeCheck(mi, state){
2969        if(state){
2970            var g = groups[mi.group];
2971            for(var i = 0, l = g.length; i < l; i++){
2972                if(g[i] != mi){
2973                    g[i].setChecked(false);
2974                }
2975            }
2976        }
2977    }
2978
2979    return {
2980
2981        /**
2982         * Hides all menus that are currently visible
2983         */
2984        hideAll : function(){
2985             hideAll();  
2986        },
2987
2988        // private
2989        register : function(menu){
2990            if(!menus){
2991                init();
2992            }
2993            menus[menu.id] = menu;
2994            menu.on("beforehide", onBeforeHide);
2995            menu.on("hide", onHide);
2996            menu.on("beforeshow", onBeforeShow);
2997            menu.on("show", onShow);
2998            var g = menu.group;
2999            if(g && menu.events["checkchange"]){
3000                if(!groups[g]){
3001                    groups[g] = [];
3002                }
3003                groups[g].push(menu);
3004                menu.on("checkchange", onCheck);
3005            }
3006        },
3007
3008         /**
3009          * Returns a {@link Roo.menu.Menu} object
3010          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3011          * be used to generate and return a new Menu instance.
3012          */
3013        get : function(menu){
3014            if(typeof menu == "string"){ // menu id
3015                return menus[menu];
3016            }else if(menu.events){  // menu instance
3017                return menu;
3018            }
3019            /*else if(typeof menu.length == 'number'){ // array of menu items?
3020                return new Roo.bootstrap.Menu({items:menu});
3021            }else{ // otherwise, must be a config
3022                return new Roo.bootstrap.Menu(menu);
3023            }
3024            */
3025            return false;
3026        },
3027
3028        // private
3029        unregister : function(menu){
3030            delete menus[menu.id];
3031            menu.un("beforehide", onBeforeHide);
3032            menu.un("hide", onHide);
3033            menu.un("beforeshow", onBeforeShow);
3034            menu.un("show", onShow);
3035            var g = menu.group;
3036            if(g && menu.events["checkchange"]){
3037                groups[g].remove(menu);
3038                menu.un("checkchange", onCheck);
3039            }
3040        },
3041
3042        // private
3043        registerCheckable : function(menuItem){
3044            var g = menuItem.group;
3045            if(g){
3046                if(!groups[g]){
3047                    groups[g] = [];
3048                }
3049                groups[g].push(menuItem);
3050                menuItem.on("beforecheckchange", onBeforeCheck);
3051            }
3052        },
3053
3054        // private
3055        unregisterCheckable : function(menuItem){
3056            var g = menuItem.group;
3057            if(g){
3058                groups[g].remove(menuItem);
3059                menuItem.un("beforecheckchange", onBeforeCheck);
3060            }
3061        }
3062    };
3063 }();/*
3064  * - LGPL
3065  *
3066  * menu
3067  * 
3068  */
3069
3070 /**
3071  * @class Roo.bootstrap.Menu
3072  * @extends Roo.bootstrap.Component
3073  * Bootstrap Menu class - container for MenuItems
3074  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3075  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3076  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3077  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3078  * 
3079  * @constructor
3080  * Create a new Menu
3081  * @param {Object} config The config object
3082  */
3083
3084
3085 Roo.bootstrap.Menu = function(config){
3086     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3087     if (this.registerMenu && this.type != 'treeview')  {
3088         Roo.bootstrap.MenuMgr.register(this);
3089     }
3090     
3091     
3092     this.addEvents({
3093         /**
3094          * @event beforeshow
3095          * Fires before this menu is displayed (return false to block)
3096          * @param {Roo.menu.Menu} this
3097          */
3098         beforeshow : true,
3099         /**
3100          * @event beforehide
3101          * Fires before this menu is hidden (return false to block)
3102          * @param {Roo.menu.Menu} this
3103          */
3104         beforehide : true,
3105         /**
3106          * @event show
3107          * Fires after this menu is displayed
3108          * @param {Roo.menu.Menu} this
3109          */
3110         show : true,
3111         /**
3112          * @event hide
3113          * Fires after this menu is hidden
3114          * @param {Roo.menu.Menu} this
3115          */
3116         hide : true,
3117         /**
3118          * @event click
3119          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3120          * @param {Roo.menu.Menu} this
3121          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3122          * @param {Roo.EventObject} e
3123          */
3124         click : true,
3125         /**
3126          * @event mouseover
3127          * Fires when the mouse is hovering over this menu
3128          * @param {Roo.menu.Menu} this
3129          * @param {Roo.EventObject} e
3130          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3131          */
3132         mouseover : true,
3133         /**
3134          * @event mouseout
3135          * Fires when the mouse exits this menu
3136          * @param {Roo.menu.Menu} this
3137          * @param {Roo.EventObject} e
3138          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3139          */
3140         mouseout : true,
3141         /**
3142          * @event itemclick
3143          * Fires when a menu item contained in this menu is clicked
3144          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3145          * @param {Roo.EventObject} e
3146          */
3147         itemclick: true
3148     });
3149     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3150 };
3151
3152 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3153     
3154    /// html : false,
3155     //align : '',
3156     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3157     type: false,
3158     /**
3159      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3160      */
3161     registerMenu : true,
3162     
3163     menuItems :false, // stores the menu items..
3164     
3165     hidden:true,
3166         
3167     parentMenu : false,
3168     
3169     stopEvent : true,
3170     
3171     isLink : false,
3172     
3173     getChildContainer : function() {
3174         return this.el;  
3175     },
3176     
3177     getAutoCreate : function(){
3178          
3179         //if (['right'].indexOf(this.align)!==-1) {
3180         //    cfg.cn[1].cls += ' pull-right'
3181         //}
3182         
3183         
3184         var cfg = {
3185             tag : 'ul',
3186             cls : 'dropdown-menu' ,
3187             style : 'z-index:1000'
3188             
3189         };
3190         
3191         if (this.type === 'submenu') {
3192             cfg.cls = 'submenu active';
3193         }
3194         if (this.type === 'treeview') {
3195             cfg.cls = 'treeview-menu';
3196         }
3197         
3198         return cfg;
3199     },
3200     initEvents : function() {
3201         
3202        // Roo.log("ADD event");
3203        // Roo.log(this.triggerEl.dom);
3204         
3205         this.triggerEl.on('click', this.onTriggerClick, this);
3206         
3207         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3208         
3209         
3210         if (this.triggerEl.hasClass('nav-item')) {
3211             // dropdown toggle on the 'a' in BS4?
3212             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3213         } else {
3214             this.triggerEl.addClass('dropdown-toggle');
3215         }
3216         if (Roo.isTouch) {
3217             this.el.on('touchstart'  , this.onTouch, this);
3218         }
3219         this.el.on('click' , this.onClick, this);
3220
3221         this.el.on("mouseover", this.onMouseOver, this);
3222         this.el.on("mouseout", this.onMouseOut, this);
3223         
3224     },
3225     
3226     findTargetItem : function(e)
3227     {
3228         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3229         if(!t){
3230             return false;
3231         }
3232         //Roo.log(t);         Roo.log(t.id);
3233         if(t && t.id){
3234             //Roo.log(this.menuitems);
3235             return this.menuitems.get(t.id);
3236             
3237             //return this.items.get(t.menuItemId);
3238         }
3239         
3240         return false;
3241     },
3242     
3243     onTouch : function(e) 
3244     {
3245         Roo.log("menu.onTouch");
3246         //e.stopEvent(); this make the user popdown broken
3247         this.onClick(e);
3248     },
3249     
3250     onClick : function(e)
3251     {
3252         Roo.log("menu.onClick");
3253         
3254         var t = this.findTargetItem(e);
3255         if(!t || t.isContainer){
3256             return;
3257         }
3258         Roo.log(e);
3259         /*
3260         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3261             if(t == this.activeItem && t.shouldDeactivate(e)){
3262                 this.activeItem.deactivate();
3263                 delete this.activeItem;
3264                 return;
3265             }
3266             if(t.canActivate){
3267                 this.setActiveItem(t, true);
3268             }
3269             return;
3270             
3271             
3272         }
3273         */
3274        
3275         Roo.log('pass click event');
3276         
3277         t.onClick(e);
3278         
3279         this.fireEvent("click", this, t, e);
3280         
3281         var _this = this;
3282         
3283         if(!t.href.length || t.href == '#'){
3284             (function() { _this.hide(); }).defer(100);
3285         }
3286         
3287     },
3288     
3289     onMouseOver : function(e){
3290         var t  = this.findTargetItem(e);
3291         //Roo.log(t);
3292         //if(t){
3293         //    if(t.canActivate && !t.disabled){
3294         //        this.setActiveItem(t, true);
3295         //    }
3296         //}
3297         
3298         this.fireEvent("mouseover", this, e, t);
3299     },
3300     isVisible : function(){
3301         return !this.hidden;
3302     },
3303     onMouseOut : function(e){
3304         var t  = this.findTargetItem(e);
3305         
3306         //if(t ){
3307         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3308         //        this.activeItem.deactivate();
3309         //        delete this.activeItem;
3310         //    }
3311         //}
3312         this.fireEvent("mouseout", this, e, t);
3313     },
3314     
3315     
3316     /**
3317      * Displays this menu relative to another element
3318      * @param {String/HTMLElement/Roo.Element} element The element to align to
3319      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3320      * the element (defaults to this.defaultAlign)
3321      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3322      */
3323     show : function(el, pos, parentMenu)
3324     {
3325         if (false === this.fireEvent("beforeshow", this)) {
3326             Roo.log("show canceled");
3327             return;
3328         }
3329         this.parentMenu = parentMenu;
3330         if(!this.el){
3331             this.render();
3332         }
3333         
3334         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3335     },
3336      /**
3337      * Displays this menu at a specific xy position
3338      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3339      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3340      */
3341     showAt : function(xy, parentMenu, /* private: */_e){
3342         this.parentMenu = parentMenu;
3343         if(!this.el){
3344             this.render();
3345         }
3346         if(_e !== false){
3347             this.fireEvent("beforeshow", this);
3348             //xy = this.el.adjustForConstraints(xy);
3349         }
3350         
3351         //this.el.show();
3352         this.hideMenuItems();
3353         this.hidden = false;
3354         this.triggerEl.addClass('open');
3355         this.el.addClass('show');
3356         
3357         // reassign x when hitting right
3358         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3359             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3360         }
3361         
3362         // reassign y when hitting bottom
3363         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3364             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3365         }
3366         
3367         // but the list may align on trigger left or trigger top... should it be a properity?
3368         
3369         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3370             this.el.setXY(xy);
3371         }
3372         
3373         this.focus();
3374         this.fireEvent("show", this);
3375     },
3376     
3377     focus : function(){
3378         return;
3379         if(!this.hidden){
3380             this.doFocus.defer(50, this);
3381         }
3382     },
3383
3384     doFocus : function(){
3385         if(!this.hidden){
3386             this.focusEl.focus();
3387         }
3388     },
3389
3390     /**
3391      * Hides this menu and optionally all parent menus
3392      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3393      */
3394     hide : function(deep)
3395     {
3396         if (false === this.fireEvent("beforehide", this)) {
3397             Roo.log("hide canceled");
3398             return;
3399         }
3400         this.hideMenuItems();
3401         if(this.el && this.isVisible()){
3402            
3403             if(this.activeItem){
3404                 this.activeItem.deactivate();
3405                 this.activeItem = null;
3406             }
3407             this.triggerEl.removeClass('open');;
3408             this.el.removeClass('show');
3409             this.hidden = true;
3410             this.fireEvent("hide", this);
3411         }
3412         if(deep === true && this.parentMenu){
3413             this.parentMenu.hide(true);
3414         }
3415     },
3416     
3417     onTriggerClick : function(e)
3418     {
3419         Roo.log('trigger click');
3420         
3421         var target = e.getTarget();
3422         
3423         Roo.log(target.nodeName.toLowerCase());
3424         
3425         if(target.nodeName.toLowerCase() === 'i'){
3426             e.preventDefault();
3427         }
3428         
3429     },
3430     
3431     onTriggerPress  : function(e)
3432     {
3433         Roo.log('trigger press');
3434         //Roo.log(e.getTarget());
3435        // Roo.log(this.triggerEl.dom);
3436        
3437         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3438         var pel = Roo.get(e.getTarget());
3439         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3440             Roo.log('is treeview or dropdown?');
3441             return;
3442         }
3443         
3444         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3445             return;
3446         }
3447         
3448         if (this.isVisible()) {
3449             Roo.log('hide');
3450             this.hide();
3451         } else {
3452             Roo.log('show');
3453             this.show(this.triggerEl, '?', false);
3454         }
3455         
3456         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3457             e.stopEvent();
3458         }
3459         
3460     },
3461        
3462     
3463     hideMenuItems : function()
3464     {
3465         Roo.log("hide Menu Items");
3466         if (!this.el) { 
3467             return;
3468         }
3469         
3470         this.el.select('.open',true).each(function(aa) {
3471             
3472             aa.removeClass('open');
3473          
3474         });
3475     },
3476     addxtypeChild : function (tree, cntr) {
3477         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3478           
3479         this.menuitems.add(comp);
3480         return comp;
3481
3482     },
3483     getEl : function()
3484     {
3485         Roo.log(this.el);
3486         return this.el;
3487     },
3488     
3489     clear : function()
3490     {
3491         this.getEl().dom.innerHTML = '';
3492         this.menuitems.clear();
3493     }
3494 });
3495
3496  
3497  /*
3498  * - LGPL
3499  *
3500  * menu item
3501  * 
3502  */
3503
3504
3505 /**
3506  * @class Roo.bootstrap.MenuItem
3507  * @extends Roo.bootstrap.Component
3508  * Bootstrap MenuItem class
3509  * @cfg {String} html the menu label
3510  * @cfg {String} href the link
3511  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3512  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3513  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3514  * @cfg {String} fa favicon to show on left of menu item.
3515  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3516  * 
3517  * 
3518  * @constructor
3519  * Create a new MenuItem
3520  * @param {Object} config The config object
3521  */
3522
3523
3524 Roo.bootstrap.MenuItem = function(config){
3525     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3526     this.addEvents({
3527         // raw events
3528         /**
3529          * @event click
3530          * The raw click event for the entire grid.
3531          * @param {Roo.bootstrap.MenuItem} this
3532          * @param {Roo.EventObject} e
3533          */
3534         "click" : true
3535     });
3536 };
3537
3538 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3539     
3540     href : false,
3541     html : false,
3542     preventDefault: false,
3543     isContainer : false,
3544     active : false,
3545     fa: false,
3546     
3547     getAutoCreate : function(){
3548         
3549         if(this.isContainer){
3550             return {
3551                 tag: 'li',
3552                 cls: 'dropdown-menu-item '
3553             };
3554         }
3555         var ctag = {
3556             tag: 'span',
3557             html: 'Link'
3558         };
3559         
3560         var anc = {
3561             tag : 'a',
3562             cls : 'dropdown-item',
3563             href : '#',
3564             cn : [  ]
3565         };
3566         
3567         if (this.fa !== false) {
3568             anc.cn.push({
3569                 tag : 'i',
3570                 cls : 'fa fa-' + this.fa
3571             });
3572         }
3573         
3574         anc.cn.push(ctag);
3575         
3576         
3577         var cfg= {
3578             tag: 'li',
3579             cls: 'dropdown-menu-item',
3580             cn: [ anc ]
3581         };
3582         if (this.parent().type == 'treeview') {
3583             cfg.cls = 'treeview-menu';
3584         }
3585         if (this.active) {
3586             cfg.cls += ' active';
3587         }
3588         
3589         
3590         
3591         anc.href = this.href || cfg.cn[0].href ;
3592         ctag.html = this.html || cfg.cn[0].html ;
3593         return cfg;
3594     },
3595     
3596     initEvents: function()
3597     {
3598         if (this.parent().type == 'treeview') {
3599             this.el.select('a').on('click', this.onClick, this);
3600         }
3601         
3602         if (this.menu) {
3603             this.menu.parentType = this.xtype;
3604             this.menu.triggerEl = this.el;
3605             this.menu = this.addxtype(Roo.apply({}, this.menu));
3606         }
3607         
3608     },
3609     onClick : function(e)
3610     {
3611         Roo.log('item on click ');
3612         
3613         if(this.preventDefault){
3614             e.preventDefault();
3615         }
3616         //this.parent().hideMenuItems();
3617         
3618         this.fireEvent('click', this, e);
3619     },
3620     getEl : function()
3621     {
3622         return this.el;
3623     } 
3624 });
3625
3626  
3627
3628  /*
3629  * - LGPL
3630  *
3631  * menu separator
3632  * 
3633  */
3634
3635
3636 /**
3637  * @class Roo.bootstrap.MenuSeparator
3638  * @extends Roo.bootstrap.Component
3639  * Bootstrap MenuSeparator class
3640  * 
3641  * @constructor
3642  * Create a new MenuItem
3643  * @param {Object} config The config object
3644  */
3645
3646
3647 Roo.bootstrap.MenuSeparator = function(config){
3648     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3649 };
3650
3651 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3652     
3653     getAutoCreate : function(){
3654         var cfg = {
3655             cls: 'divider',
3656             tag : 'li'
3657         };
3658         
3659         return cfg;
3660     }
3661    
3662 });
3663
3664  
3665
3666  
3667 /*
3668 * Licence: LGPL
3669 */
3670
3671 /**
3672  * @class Roo.bootstrap.Modal
3673  * @extends Roo.bootstrap.Component
3674  * Bootstrap Modal class
3675  * @cfg {String} title Title of dialog
3676  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3677  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3678  * @cfg {Boolean} specificTitle default false
3679  * @cfg {Array} buttons Array of buttons or standard button set..
3680  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3681  * @cfg {Boolean} animate default true
3682  * @cfg {Boolean} allow_close default true
3683  * @cfg {Boolean} fitwindow default false
3684  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3685  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3686  * @cfg {String} size (sm|lg) default empty
3687  * @cfg {Number} max_width set the max width of modal
3688  *
3689  *
3690  * @constructor
3691  * Create a new Modal Dialog
3692  * @param {Object} config The config object
3693  */
3694
3695 Roo.bootstrap.Modal = function(config){
3696     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3697     this.addEvents({
3698         // raw events
3699         /**
3700          * @event btnclick
3701          * The raw btnclick event for the button
3702          * @param {Roo.EventObject} e
3703          */
3704         "btnclick" : true,
3705         /**
3706          * @event resize
3707          * Fire when dialog resize
3708          * @param {Roo.bootstrap.Modal} this
3709          * @param {Roo.EventObject} e
3710          */
3711         "resize" : true
3712     });
3713     this.buttons = this.buttons || [];
3714
3715     if (this.tmpl) {
3716         this.tmpl = Roo.factory(this.tmpl);
3717     }
3718
3719 };
3720
3721 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3722
3723     title : 'test dialog',
3724
3725     buttons : false,
3726
3727     // set on load...
3728
3729     html: false,
3730
3731     tmp: false,
3732
3733     specificTitle: false,
3734
3735     buttonPosition: 'right',
3736
3737     allow_close : true,
3738
3739     animate : true,
3740
3741     fitwindow: false,
3742     
3743      // private
3744     dialogEl: false,
3745     bodyEl:  false,
3746     footerEl:  false,
3747     titleEl:  false,
3748     closeEl:  false,
3749
3750     size: '',
3751     
3752     max_width: 0,
3753     
3754     max_height: 0,
3755     
3756     fit_content: false,
3757
3758     onRender : function(ct, position)
3759     {
3760         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3761
3762         if(!this.el){
3763             var cfg = Roo.apply({},  this.getAutoCreate());
3764             cfg.id = Roo.id();
3765             //if(!cfg.name){
3766             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3767             //}
3768             //if (!cfg.name.length) {
3769             //    delete cfg.name;
3770            // }
3771             if (this.cls) {
3772                 cfg.cls += ' ' + this.cls;
3773             }
3774             if (this.style) {
3775                 cfg.style = this.style;
3776             }
3777             this.el = Roo.get(document.body).createChild(cfg, position);
3778         }
3779         //var type = this.el.dom.type;
3780
3781
3782         if(this.tabIndex !== undefined){
3783             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3784         }
3785
3786         this.dialogEl = this.el.select('.modal-dialog',true).first();
3787         this.bodyEl = this.el.select('.modal-body',true).first();
3788         this.closeEl = this.el.select('.modal-header .close', true).first();
3789         this.headerEl = this.el.select('.modal-header',true).first();
3790         this.titleEl = this.el.select('.modal-title',true).first();
3791         this.footerEl = this.el.select('.modal-footer',true).first();
3792
3793         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3794         
3795         //this.el.addClass("x-dlg-modal");
3796
3797         if (this.buttons.length) {
3798             Roo.each(this.buttons, function(bb) {
3799                 var b = Roo.apply({}, bb);
3800                 b.xns = b.xns || Roo.bootstrap;
3801                 b.xtype = b.xtype || 'Button';
3802                 if (typeof(b.listeners) == 'undefined') {
3803                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3804                 }
3805
3806                 var btn = Roo.factory(b);
3807
3808                 btn.render(this.getButtonContainer());
3809
3810             },this);
3811         }
3812         // render the children.
3813         var nitems = [];
3814
3815         if(typeof(this.items) != 'undefined'){
3816             var items = this.items;
3817             delete this.items;
3818
3819             for(var i =0;i < items.length;i++) {
3820                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3821             }
3822         }
3823
3824         this.items = nitems;
3825
3826         // where are these used - they used to be body/close/footer
3827
3828
3829         this.initEvents();
3830         //this.el.addClass([this.fieldClass, this.cls]);
3831
3832     },
3833
3834     getAutoCreate : function()
3835     {
3836         // we will default to modal-body-overflow - might need to remove or make optional later.
3837         var bdy = {
3838                 cls : 'modal-body enable-modal-body-overflow ', 
3839                 html : this.html || ''
3840         };
3841
3842         var title = {
3843             tag: 'h4',
3844             cls : 'modal-title',
3845             html : this.title
3846         };
3847
3848         if(this.specificTitle){
3849             title = this.title;
3850
3851         }
3852
3853         var header = [];
3854         if (this.allow_close && Roo.bootstrap.version == 3) {
3855             header.push({
3856                 tag: 'button',
3857                 cls : 'close',
3858                 html : '&times'
3859             });
3860         }
3861
3862         header.push(title);
3863
3864         if (this.allow_close && Roo.bootstrap.version == 4) {
3865             header.push({
3866                 tag: 'button',
3867                 cls : 'close',
3868                 html : '&times'
3869             });
3870         }
3871         
3872         var size = '';
3873
3874         if(this.size.length){
3875             size = 'modal-' + this.size;
3876         }
3877         
3878         var footer = Roo.bootstrap.version == 3 ?
3879             {
3880                 cls : 'modal-footer',
3881                 cn : [
3882                     {
3883                         tag: 'div',
3884                         cls: 'btn-' + this.buttonPosition
3885                     }
3886                 ]
3887
3888             } :
3889             {  // BS4 uses mr-auto on left buttons....
3890                 cls : 'modal-footer'
3891             };
3892
3893             
3894
3895         
3896         
3897         var modal = {
3898             cls: "modal",
3899              cn : [
3900                 {
3901                     cls: "modal-dialog " + size,
3902                     cn : [
3903                         {
3904                             cls : "modal-content",
3905                             cn : [
3906                                 {
3907                                     cls : 'modal-header',
3908                                     cn : header
3909                                 },
3910                                 bdy,
3911                                 footer
3912                             ]
3913
3914                         }
3915                     ]
3916
3917                 }
3918             ]
3919         };
3920
3921         if(this.animate){
3922             modal.cls += ' fade';
3923         }
3924
3925         return modal;
3926
3927     },
3928     getChildContainer : function() {
3929
3930          return this.bodyEl;
3931
3932     },
3933     getButtonContainer : function() {
3934         
3935          return Roo.bootstrap.version == 4 ?
3936             this.el.select('.modal-footer',true).first()
3937             : this.el.select('.modal-footer div',true).first();
3938
3939     },
3940     initEvents : function()
3941     {
3942         if (this.allow_close) {
3943             this.closeEl.on('click', this.hide, this);
3944         }
3945         Roo.EventManager.onWindowResize(this.resize, this, true);
3946
3947
3948     },
3949   
3950
3951     resize : function()
3952     {
3953         this.maskEl.setSize(
3954             Roo.lib.Dom.getViewWidth(true),
3955             Roo.lib.Dom.getViewHeight(true)
3956         );
3957         
3958         if (this.fitwindow) {
3959             
3960            
3961             this.setSize(
3962                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3963                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3964             );
3965             return;
3966         }
3967         
3968         if(this.max_width !== 0) {
3969             
3970             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3971             
3972             if(this.height) {
3973                 this.setSize(w, this.height);
3974                 return;
3975             }
3976             
3977             if(this.max_height) {
3978                 this.setSize(w,Math.min(
3979                     this.max_height,
3980                     Roo.lib.Dom.getViewportHeight(true) - 60
3981                 ));
3982                 
3983                 return;
3984             }
3985             
3986             if(!this.fit_content) {
3987                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3988                 return;
3989             }
3990             
3991             this.setSize(w, Math.min(
3992                 60 +
3993                 this.headerEl.getHeight() + 
3994                 this.footerEl.getHeight() + 
3995                 this.getChildHeight(this.bodyEl.dom.childNodes),
3996                 Roo.lib.Dom.getViewportHeight(true) - 60)
3997             );
3998         }
3999         
4000     },
4001
4002     setSize : function(w,h)
4003     {
4004         if (!w && !h) {
4005             return;
4006         }
4007         
4008         this.resizeTo(w,h);
4009     },
4010
4011     show : function() {
4012
4013         if (!this.rendered) {
4014             this.render();
4015         }
4016
4017         //this.el.setStyle('display', 'block');
4018         this.el.removeClass('hideing');
4019         this.el.dom.style.display='block';
4020         
4021         Roo.get(document.body).addClass('modal-open');
4022  
4023         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4024             
4025             (function(){
4026                 this.el.addClass('show');
4027                 this.el.addClass('in');
4028             }).defer(50, this);
4029         }else{
4030             this.el.addClass('show');
4031             this.el.addClass('in');
4032         }
4033
4034         // not sure how we can show data in here..
4035         //if (this.tmpl) {
4036         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4037         //}
4038
4039         Roo.get(document.body).addClass("x-body-masked");
4040         
4041         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4042         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4043         this.maskEl.dom.style.display = 'block';
4044         this.maskEl.addClass('show');
4045         
4046         
4047         this.resize();
4048         
4049         this.fireEvent('show', this);
4050
4051         // set zindex here - otherwise it appears to be ignored...
4052         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4053
4054         (function () {
4055             this.items.forEach( function(e) {
4056                 e.layout ? e.layout() : false;
4057
4058             });
4059         }).defer(100,this);
4060
4061     },
4062     hide : function()
4063     {
4064         if(this.fireEvent("beforehide", this) !== false){
4065             
4066             this.maskEl.removeClass('show');
4067             
4068             this.maskEl.dom.style.display = '';
4069             Roo.get(document.body).removeClass("x-body-masked");
4070             this.el.removeClass('in');
4071             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4072
4073             if(this.animate){ // why
4074                 this.el.addClass('hideing');
4075                 this.el.removeClass('show');
4076                 (function(){
4077                     if (!this.el.hasClass('hideing')) {
4078                         return; // it's been shown again...
4079                     }
4080                     
4081                     this.el.dom.style.display='';
4082
4083                     Roo.get(document.body).removeClass('modal-open');
4084                     this.el.removeClass('hideing');
4085                 }).defer(150,this);
4086                 
4087             }else{
4088                 this.el.removeClass('show');
4089                 this.el.dom.style.display='';
4090                 Roo.get(document.body).removeClass('modal-open');
4091
4092             }
4093             this.fireEvent('hide', this);
4094         }
4095     },
4096     isVisible : function()
4097     {
4098         
4099         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4100         
4101     },
4102
4103     addButton : function(str, cb)
4104     {
4105
4106
4107         var b = Roo.apply({}, { html : str } );
4108         b.xns = b.xns || Roo.bootstrap;
4109         b.xtype = b.xtype || 'Button';
4110         if (typeof(b.listeners) == 'undefined') {
4111             b.listeners = { click : cb.createDelegate(this)  };
4112         }
4113
4114         var btn = Roo.factory(b);
4115
4116         btn.render(this.getButtonContainer());
4117
4118         return btn;
4119
4120     },
4121
4122     setDefaultButton : function(btn)
4123     {
4124         //this.el.select('.modal-footer').()
4125     },
4126
4127     resizeTo: function(w,h)
4128     {
4129         this.dialogEl.setWidth(w);
4130         
4131         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4132
4133         this.bodyEl.setHeight(h - diff);
4134         
4135         this.fireEvent('resize', this);
4136     },
4137     
4138     setContentSize  : function(w, h)
4139     {
4140
4141     },
4142     onButtonClick: function(btn,e)
4143     {
4144         //Roo.log([a,b,c]);
4145         this.fireEvent('btnclick', btn.name, e);
4146     },
4147      /**
4148      * Set the title of the Dialog
4149      * @param {String} str new Title
4150      */
4151     setTitle: function(str) {
4152         this.titleEl.dom.innerHTML = str;
4153     },
4154     /**
4155      * Set the body of the Dialog
4156      * @param {String} str new Title
4157      */
4158     setBody: function(str) {
4159         this.bodyEl.dom.innerHTML = str;
4160     },
4161     /**
4162      * Set the body of the Dialog using the template
4163      * @param {Obj} data - apply this data to the template and replace the body contents.
4164      */
4165     applyBody: function(obj)
4166     {
4167         if (!this.tmpl) {
4168             Roo.log("Error - using apply Body without a template");
4169             //code
4170         }
4171         this.tmpl.overwrite(this.bodyEl, obj);
4172     },
4173     
4174     getChildHeight : function(child_nodes)
4175     {
4176         if(
4177             !child_nodes ||
4178             child_nodes.length == 0
4179         ) {
4180             return;
4181         }
4182         
4183         var child_height = 0;
4184         
4185         for(var i = 0; i < child_nodes.length; i++) {
4186             
4187             /*
4188             * for modal with tabs...
4189             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4190                 
4191                 var layout_childs = child_nodes[i].childNodes;
4192                 
4193                 for(var j = 0; j < layout_childs.length; j++) {
4194                     
4195                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4196                         
4197                         var layout_body_childs = layout_childs[j].childNodes;
4198                         
4199                         for(var k = 0; k < layout_body_childs.length; k++) {
4200                             
4201                             if(layout_body_childs[k].classList.contains('navbar')) {
4202                                 child_height += layout_body_childs[k].offsetHeight;
4203                                 continue;
4204                             }
4205                             
4206                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4207                                 
4208                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4209                                 
4210                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4211                                     
4212                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4213                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4214                                         continue;
4215                                     }
4216                                     
4217                                 }
4218                                 
4219                             }
4220                             
4221                         }
4222                     }
4223                 }
4224                 continue;
4225             }
4226             */
4227             
4228             child_height += child_nodes[i].offsetHeight;
4229             // Roo.log(child_nodes[i].offsetHeight);
4230         }
4231         
4232         return child_height;
4233     }
4234
4235 });
4236
4237
4238 Roo.apply(Roo.bootstrap.Modal,  {
4239     /**
4240          * Button config that displays a single OK button
4241          * @type Object
4242          */
4243         OK :  [{
4244             name : 'ok',
4245             weight : 'primary',
4246             html : 'OK'
4247         }],
4248         /**
4249          * Button config that displays Yes and No buttons
4250          * @type Object
4251          */
4252         YESNO : [
4253             {
4254                 name  : 'no',
4255                 html : 'No'
4256             },
4257             {
4258                 name  :'yes',
4259                 weight : 'primary',
4260                 html : 'Yes'
4261             }
4262         ],
4263
4264         /**
4265          * Button config that displays OK and Cancel buttons
4266          * @type Object
4267          */
4268         OKCANCEL : [
4269             {
4270                name : 'cancel',
4271                 html : 'Cancel'
4272             },
4273             {
4274                 name : 'ok',
4275                 weight : 'primary',
4276                 html : 'OK'
4277             }
4278         ],
4279         /**
4280          * Button config that displays Yes, No and Cancel buttons
4281          * @type Object
4282          */
4283         YESNOCANCEL : [
4284             {
4285                 name : 'yes',
4286                 weight : 'primary',
4287                 html : 'Yes'
4288             },
4289             {
4290                 name : 'no',
4291                 html : 'No'
4292             },
4293             {
4294                 name : 'cancel',
4295                 html : 'Cancel'
4296             }
4297         ],
4298         
4299         zIndex : 10001
4300 });
4301 /*
4302  * - LGPL
4303  *
4304  * messagebox - can be used as a replace
4305  * 
4306  */
4307 /**
4308  * @class Roo.MessageBox
4309  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4310  * Example usage:
4311  *<pre><code>
4312 // Basic alert:
4313 Roo.Msg.alert('Status', 'Changes saved successfully.');
4314
4315 // Prompt for user data:
4316 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4317     if (btn == 'ok'){
4318         // process text value...
4319     }
4320 });
4321
4322 // Show a dialog using config options:
4323 Roo.Msg.show({
4324    title:'Save Changes?',
4325    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4326    buttons: Roo.Msg.YESNOCANCEL,
4327    fn: processResult,
4328    animEl: 'elId'
4329 });
4330 </code></pre>
4331  * @singleton
4332  */
4333 Roo.bootstrap.MessageBox = function(){
4334     var dlg, opt, mask, waitTimer;
4335     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4336     var buttons, activeTextEl, bwidth;
4337
4338     
4339     // private
4340     var handleButton = function(button){
4341         dlg.hide();
4342         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4343     };
4344
4345     // private
4346     var handleHide = function(){
4347         if(opt && opt.cls){
4348             dlg.el.removeClass(opt.cls);
4349         }
4350         //if(waitTimer){
4351         //    Roo.TaskMgr.stop(waitTimer);
4352         //    waitTimer = null;
4353         //}
4354     };
4355
4356     // private
4357     var updateButtons = function(b){
4358         var width = 0;
4359         if(!b){
4360             buttons["ok"].hide();
4361             buttons["cancel"].hide();
4362             buttons["yes"].hide();
4363             buttons["no"].hide();
4364             dlg.footerEl.hide();
4365             
4366             return width;
4367         }
4368         dlg.footerEl.show();
4369         for(var k in buttons){
4370             if(typeof buttons[k] != "function"){
4371                 if(b[k]){
4372                     buttons[k].show();
4373                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4374                     width += buttons[k].el.getWidth()+15;
4375                 }else{
4376                     buttons[k].hide();
4377                 }
4378             }
4379         }
4380         return width;
4381     };
4382
4383     // private
4384     var handleEsc = function(d, k, e){
4385         if(opt && opt.closable !== false){
4386             dlg.hide();
4387         }
4388         if(e){
4389             e.stopEvent();
4390         }
4391     };
4392
4393     return {
4394         /**
4395          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4396          * @return {Roo.BasicDialog} The BasicDialog element
4397          */
4398         getDialog : function(){
4399            if(!dlg){
4400                 dlg = new Roo.bootstrap.Modal( {
4401                     //draggable: true,
4402                     //resizable:false,
4403                     //constraintoviewport:false,
4404                     //fixedcenter:true,
4405                     //collapsible : false,
4406                     //shim:true,
4407                     //modal: true,
4408                 //    width: 'auto',
4409                   //  height:100,
4410                     //buttonAlign:"center",
4411                     closeClick : function(){
4412                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4413                             handleButton("no");
4414                         }else{
4415                             handleButton("cancel");
4416                         }
4417                     }
4418                 });
4419                 dlg.render();
4420                 dlg.on("hide", handleHide);
4421                 mask = dlg.mask;
4422                 //dlg.addKeyListener(27, handleEsc);
4423                 buttons = {};
4424                 this.buttons = buttons;
4425                 var bt = this.buttonText;
4426                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4427                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4428                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4429                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4430                 //Roo.log(buttons);
4431                 bodyEl = dlg.bodyEl.createChild({
4432
4433                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4434                         '<textarea class="roo-mb-textarea"></textarea>' +
4435                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4436                 });
4437                 msgEl = bodyEl.dom.firstChild;
4438                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4439                 textboxEl.enableDisplayMode();
4440                 textboxEl.addKeyListener([10,13], function(){
4441                     if(dlg.isVisible() && opt && opt.buttons){
4442                         if(opt.buttons.ok){
4443                             handleButton("ok");
4444                         }else if(opt.buttons.yes){
4445                             handleButton("yes");
4446                         }
4447                     }
4448                 });
4449                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4450                 textareaEl.enableDisplayMode();
4451                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4452                 progressEl.enableDisplayMode();
4453                 
4454                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4455                 var pf = progressEl.dom.firstChild;
4456                 if (pf) {
4457                     pp = Roo.get(pf.firstChild);
4458                     pp.setHeight(pf.offsetHeight);
4459                 }
4460                 
4461             }
4462             return dlg;
4463         },
4464
4465         /**
4466          * Updates the message box body text
4467          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4468          * the XHTML-compliant non-breaking space character '&amp;#160;')
4469          * @return {Roo.MessageBox} This message box
4470          */
4471         updateText : function(text)
4472         {
4473             if(!dlg.isVisible() && !opt.width){
4474                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4475                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4476             }
4477             msgEl.innerHTML = text || '&#160;';
4478       
4479             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4480             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4481             var w = Math.max(
4482                     Math.min(opt.width || cw , this.maxWidth), 
4483                     Math.max(opt.minWidth || this.minWidth, bwidth)
4484             );
4485             if(opt.prompt){
4486                 activeTextEl.setWidth(w);
4487             }
4488             if(dlg.isVisible()){
4489                 dlg.fixedcenter = false;
4490             }
4491             // to big, make it scroll. = But as usual stupid IE does not support
4492             // !important..
4493             
4494             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4495                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4496                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4497             } else {
4498                 bodyEl.dom.style.height = '';
4499                 bodyEl.dom.style.overflowY = '';
4500             }
4501             if (cw > w) {
4502                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4503             } else {
4504                 bodyEl.dom.style.overflowX = '';
4505             }
4506             
4507             dlg.setContentSize(w, bodyEl.getHeight());
4508             if(dlg.isVisible()){
4509                 dlg.fixedcenter = true;
4510             }
4511             return this;
4512         },
4513
4514         /**
4515          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4516          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4517          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4518          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4519          * @return {Roo.MessageBox} This message box
4520          */
4521         updateProgress : function(value, text){
4522             if(text){
4523                 this.updateText(text);
4524             }
4525             
4526             if (pp) { // weird bug on my firefox - for some reason this is not defined
4527                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4528                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4529             }
4530             return this;
4531         },        
4532
4533         /**
4534          * Returns true if the message box is currently displayed
4535          * @return {Boolean} True if the message box is visible, else false
4536          */
4537         isVisible : function(){
4538             return dlg && dlg.isVisible();  
4539         },
4540
4541         /**
4542          * Hides the message box if it is displayed
4543          */
4544         hide : function(){
4545             if(this.isVisible()){
4546                 dlg.hide();
4547             }  
4548         },
4549
4550         /**
4551          * Displays a new message box, or reinitializes an existing message box, based on the config options
4552          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4553          * The following config object properties are supported:
4554          * <pre>
4555 Property    Type             Description
4556 ----------  ---------------  ------------------------------------------------------------------------------------
4557 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4558                                    closes (defaults to undefined)
4559 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4560                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4561 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4562                                    progress and wait dialogs will ignore this property and always hide the
4563                                    close button as they can only be closed programmatically.
4564 cls               String           A custom CSS class to apply to the message box element
4565 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4566                                    displayed (defaults to 75)
4567 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4568                                    function will be btn (the name of the button that was clicked, if applicable,
4569                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4570                                    Progress and wait dialogs will ignore this option since they do not respond to
4571                                    user actions and can only be closed programmatically, so any required function
4572                                    should be called by the same code after it closes the dialog.
4573 icon              String           A CSS class that provides a background image to be used as an icon for
4574                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4575 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4576 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4577 modal             Boolean          False to allow user interaction with the page while the message box is
4578                                    displayed (defaults to true)
4579 msg               String           A string that will replace the existing message box body text (defaults
4580                                    to the XHTML-compliant non-breaking space character '&#160;')
4581 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4582 progress          Boolean          True to display a progress bar (defaults to false)
4583 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4584 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4585 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4586 title             String           The title text
4587 value             String           The string value to set into the active textbox element if displayed
4588 wait              Boolean          True to display a progress bar (defaults to false)
4589 width             Number           The width of the dialog in pixels
4590 </pre>
4591          *
4592          * Example usage:
4593          * <pre><code>
4594 Roo.Msg.show({
4595    title: 'Address',
4596    msg: 'Please enter your address:',
4597    width: 300,
4598    buttons: Roo.MessageBox.OKCANCEL,
4599    multiline: true,
4600    fn: saveAddress,
4601    animEl: 'addAddressBtn'
4602 });
4603 </code></pre>
4604          * @param {Object} config Configuration options
4605          * @return {Roo.MessageBox} This message box
4606          */
4607         show : function(options)
4608         {
4609             
4610             // this causes nightmares if you show one dialog after another
4611             // especially on callbacks..
4612              
4613             if(this.isVisible()){
4614                 
4615                 this.hide();
4616                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4617                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4618                 Roo.log("New Dialog Message:" +  options.msg )
4619                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4620                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4621                 
4622             }
4623             var d = this.getDialog();
4624             opt = options;
4625             d.setTitle(opt.title || "&#160;");
4626             d.closeEl.setDisplayed(opt.closable !== false);
4627             activeTextEl = textboxEl;
4628             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4629             if(opt.prompt){
4630                 if(opt.multiline){
4631                     textboxEl.hide();
4632                     textareaEl.show();
4633                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4634                         opt.multiline : this.defaultTextHeight);
4635                     activeTextEl = textareaEl;
4636                 }else{
4637                     textboxEl.show();
4638                     textareaEl.hide();
4639                 }
4640             }else{
4641                 textboxEl.hide();
4642                 textareaEl.hide();
4643             }
4644             progressEl.setDisplayed(opt.progress === true);
4645             if (opt.progress) {
4646                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4647             }
4648             this.updateProgress(0);
4649             activeTextEl.dom.value = opt.value || "";
4650             if(opt.prompt){
4651                 dlg.setDefaultButton(activeTextEl);
4652             }else{
4653                 var bs = opt.buttons;
4654                 var db = null;
4655                 if(bs && bs.ok){
4656                     db = buttons["ok"];
4657                 }else if(bs && bs.yes){
4658                     db = buttons["yes"];
4659                 }
4660                 dlg.setDefaultButton(db);
4661             }
4662             bwidth = updateButtons(opt.buttons);
4663             this.updateText(opt.msg);
4664             if(opt.cls){
4665                 d.el.addClass(opt.cls);
4666             }
4667             d.proxyDrag = opt.proxyDrag === true;
4668             d.modal = opt.modal !== false;
4669             d.mask = opt.modal !== false ? mask : false;
4670             if(!d.isVisible()){
4671                 // force it to the end of the z-index stack so it gets a cursor in FF
4672                 document.body.appendChild(dlg.el.dom);
4673                 d.animateTarget = null;
4674                 d.show(options.animEl);
4675             }
4676             return this;
4677         },
4678
4679         /**
4680          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4681          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4682          * and closing the message box when the process is complete.
4683          * @param {String} title The title bar text
4684          * @param {String} msg The message box body text
4685          * @return {Roo.MessageBox} This message box
4686          */
4687         progress : function(title, msg){
4688             this.show({
4689                 title : title,
4690                 msg : msg,
4691                 buttons: false,
4692                 progress:true,
4693                 closable:false,
4694                 minWidth: this.minProgressWidth,
4695                 modal : true
4696             });
4697             return this;
4698         },
4699
4700         /**
4701          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4702          * If a callback function is passed it will be called after the user clicks the button, and the
4703          * id of the button that was clicked will be passed as the only parameter to the callback
4704          * (could also be the top-right close button).
4705          * @param {String} title The title bar text
4706          * @param {String} msg The message box body text
4707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4708          * @param {Object} scope (optional) The scope of the callback function
4709          * @return {Roo.MessageBox} This message box
4710          */
4711         alert : function(title, msg, fn, scope)
4712         {
4713             this.show({
4714                 title : title,
4715                 msg : msg,
4716                 buttons: this.OK,
4717                 fn: fn,
4718                 closable : false,
4719                 scope : scope,
4720                 modal : true
4721             });
4722             return this;
4723         },
4724
4725         /**
4726          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4727          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4728          * You are responsible for closing the message box when the process is complete.
4729          * @param {String} msg The message box body text
4730          * @param {String} title (optional) The title bar text
4731          * @return {Roo.MessageBox} This message box
4732          */
4733         wait : function(msg, title){
4734             this.show({
4735                 title : title,
4736                 msg : msg,
4737                 buttons: false,
4738                 closable:false,
4739                 progress:true,
4740                 modal:true,
4741                 width:300,
4742                 wait:true
4743             });
4744             waitTimer = Roo.TaskMgr.start({
4745                 run: function(i){
4746                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4747                 },
4748                 interval: 1000
4749             });
4750             return this;
4751         },
4752
4753         /**
4754          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4755          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4756          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4757          * @param {String} title The title bar text
4758          * @param {String} msg The message box body text
4759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4760          * @param {Object} scope (optional) The scope of the callback function
4761          * @return {Roo.MessageBox} This message box
4762          */
4763         confirm : function(title, msg, fn, scope){
4764             this.show({
4765                 title : title,
4766                 msg : msg,
4767                 buttons: this.YESNO,
4768                 fn: fn,
4769                 scope : scope,
4770                 modal : true
4771             });
4772             return this;
4773         },
4774
4775         /**
4776          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4777          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4778          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4779          * (could also be the top-right close button) and the text that was entered will be passed as the two
4780          * parameters to the callback.
4781          * @param {String} title The title bar text
4782          * @param {String} msg The message box body text
4783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4784          * @param {Object} scope (optional) The scope of the callback function
4785          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4786          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4787          * @return {Roo.MessageBox} This message box
4788          */
4789         prompt : function(title, msg, fn, scope, multiline){
4790             this.show({
4791                 title : title,
4792                 msg : msg,
4793                 buttons: this.OKCANCEL,
4794                 fn: fn,
4795                 minWidth:250,
4796                 scope : scope,
4797                 prompt:true,
4798                 multiline: multiline,
4799                 modal : true
4800             });
4801             return this;
4802         },
4803
4804         /**
4805          * Button config that displays a single OK button
4806          * @type Object
4807          */
4808         OK : {ok:true},
4809         /**
4810          * Button config that displays Yes and No buttons
4811          * @type Object
4812          */
4813         YESNO : {yes:true, no:true},
4814         /**
4815          * Button config that displays OK and Cancel buttons
4816          * @type Object
4817          */
4818         OKCANCEL : {ok:true, cancel:true},
4819         /**
4820          * Button config that displays Yes, No and Cancel buttons
4821          * @type Object
4822          */
4823         YESNOCANCEL : {yes:true, no:true, cancel:true},
4824
4825         /**
4826          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4827          * @type Number
4828          */
4829         defaultTextHeight : 75,
4830         /**
4831          * The maximum width in pixels of the message box (defaults to 600)
4832          * @type Number
4833          */
4834         maxWidth : 600,
4835         /**
4836          * The minimum width in pixels of the message box (defaults to 100)
4837          * @type Number
4838          */
4839         minWidth : 100,
4840         /**
4841          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4842          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4843          * @type Number
4844          */
4845         minProgressWidth : 250,
4846         /**
4847          * An object containing the default button text strings that can be overriden for localized language support.
4848          * Supported properties are: ok, cancel, yes and no.
4849          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4850          * @type Object
4851          */
4852         buttonText : {
4853             ok : "OK",
4854             cancel : "Cancel",
4855             yes : "Yes",
4856             no : "No"
4857         }
4858     };
4859 }();
4860
4861 /**
4862  * Shorthand for {@link Roo.MessageBox}
4863  */
4864 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4865 Roo.Msg = Roo.Msg || Roo.MessageBox;
4866 /*
4867  * - LGPL
4868  *
4869  * navbar
4870  * 
4871  */
4872
4873 /**
4874  * @class Roo.bootstrap.Navbar
4875  * @extends Roo.bootstrap.Component
4876  * Bootstrap Navbar class
4877
4878  * @constructor
4879  * Create a new Navbar
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.Navbar = function(config){
4885     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event beforetoggle
4890          * Fire before toggle the menu
4891          * @param {Roo.EventObject} e
4892          */
4893         "beforetoggle" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4898     
4899     
4900    
4901     // private
4902     navItems : false,
4903     loadMask : false,
4904     
4905     
4906     getAutoCreate : function(){
4907         
4908         
4909         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4910         
4911     },
4912     
4913     initEvents :function ()
4914     {
4915         //Roo.log(this.el.select('.navbar-toggle',true));
4916         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4917         
4918         var mark = {
4919             tag: "div",
4920             cls:"x-dlg-mask"
4921         };
4922         
4923         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4924         
4925         var size = this.el.getSize();
4926         this.maskEl.setSize(size.width, size.height);
4927         this.maskEl.enableDisplayMode("block");
4928         this.maskEl.hide();
4929         
4930         if(this.loadMask){
4931             this.maskEl.show();
4932         }
4933     },
4934     
4935     
4936     getChildContainer : function()
4937     {
4938         if (this.el && this.el.select('.collapse').getCount()) {
4939             return this.el.select('.collapse',true).first();
4940         }
4941         
4942         return this.el;
4943     },
4944     
4945     mask : function()
4946     {
4947         this.maskEl.show();
4948     },
4949     
4950     unmask : function()
4951     {
4952         this.maskEl.hide();
4953     },
4954     onToggle : function()
4955     {
4956         
4957         if(this.fireEvent('beforetoggle', this) === false){
4958             return;
4959         }
4960         var ce = this.el.select('.navbar-collapse',true).first();
4961       
4962         if (!ce.hasClass('show')) {
4963            this.expand();
4964         } else {
4965             this.collapse();
4966         }
4967         
4968         
4969     
4970     },
4971     /**
4972      * Expand the navbar pulldown 
4973      */
4974     expand : function ()
4975     {
4976        
4977         var ce = this.el.select('.navbar-collapse',true).first();
4978         if (ce.hasClass('collapsing')) {
4979             return;
4980         }
4981         ce.dom.style.height = '';
4982                // show it...
4983         ce.addClass('in'); // old...
4984         ce.removeClass('collapse');
4985         ce.addClass('show');
4986         var h = ce.getHeight();
4987         Roo.log(h);
4988         ce.removeClass('show');
4989         // at this point we should be able to see it..
4990         ce.addClass('collapsing');
4991         
4992         ce.setHeight(0); // resize it ...
4993         ce.on('transitionend', function() {
4994             //Roo.log('done transition');
4995             ce.removeClass('collapsing');
4996             ce.addClass('show');
4997             ce.removeClass('collapse');
4998
4999             ce.dom.style.height = '';
5000         }, this, { single: true} );
5001         ce.setHeight(h);
5002         ce.dom.scrollTop = 0;
5003     },
5004     /**
5005      * Collapse the navbar pulldown 
5006      */
5007     collapse : function()
5008     {
5009          var ce = this.el.select('.navbar-collapse',true).first();
5010        
5011         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5012             // it's collapsed or collapsing..
5013             return;
5014         }
5015         ce.removeClass('in'); // old...
5016         ce.setHeight(ce.getHeight());
5017         ce.removeClass('show');
5018         ce.addClass('collapsing');
5019         
5020         ce.on('transitionend', function() {
5021             ce.dom.style.height = '';
5022             ce.removeClass('collapsing');
5023             ce.addClass('collapse');
5024         }, this, { single: true} );
5025         ce.setHeight(0);
5026     }
5027     
5028     
5029     
5030 });
5031
5032
5033
5034  
5035
5036  /*
5037  * - LGPL
5038  *
5039  * navbar
5040  * 
5041  */
5042
5043 /**
5044  * @class Roo.bootstrap.NavSimplebar
5045  * @extends Roo.bootstrap.Navbar
5046  * Bootstrap Sidebar class
5047  *
5048  * @cfg {Boolean} inverse is inverted color
5049  * 
5050  * @cfg {String} type (nav | pills | tabs)
5051  * @cfg {Boolean} arrangement stacked | justified
5052  * @cfg {String} align (left | right) alignment
5053  * 
5054  * @cfg {Boolean} main (true|false) main nav bar? default false
5055  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5056  * 
5057  * @cfg {String} tag (header|footer|nav|div) default is nav 
5058
5059  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5060  * 
5061  * 
5062  * @constructor
5063  * Create a new Sidebar
5064  * @param {Object} config The config object
5065  */
5066
5067
5068 Roo.bootstrap.NavSimplebar = function(config){
5069     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5070 };
5071
5072 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5073     
5074     inverse: false,
5075     
5076     type: false,
5077     arrangement: '',
5078     align : false,
5079     
5080     weight : 'light',
5081     
5082     main : false,
5083     
5084     
5085     tag : false,
5086     
5087     
5088     getAutoCreate : function(){
5089         
5090         
5091         var cfg = {
5092             tag : this.tag || 'div',
5093             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5094         };
5095         if (['light','white'].indexOf(this.weight) > -1) {
5096             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5097         }
5098         cfg.cls += ' bg-' + this.weight;
5099         
5100         if (this.inverse) {
5101             cfg.cls += ' navbar-inverse';
5102             
5103         }
5104         
5105         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5106         
5107         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5108             return cfg;
5109         }
5110         
5111         
5112     
5113         
5114         cfg.cn = [
5115             {
5116                 cls: 'nav nav-' + this.xtype,
5117                 tag : 'ul'
5118             }
5119         ];
5120         
5121          
5122         this.type = this.type || 'nav';
5123         if (['tabs','pills'].indexOf(this.type) != -1) {
5124             cfg.cn[0].cls += ' nav-' + this.type
5125         
5126         
5127         } else {
5128             if (this.type!=='nav') {
5129                 Roo.log('nav type must be nav/tabs/pills')
5130             }
5131             cfg.cn[0].cls += ' navbar-nav'
5132         }
5133         
5134         
5135         
5136         
5137         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5138             cfg.cn[0].cls += ' nav-' + this.arrangement;
5139         }
5140         
5141         
5142         if (this.align === 'right') {
5143             cfg.cn[0].cls += ' navbar-right';
5144         }
5145         
5146         
5147         
5148         
5149         return cfg;
5150     
5151         
5152     }
5153     
5154     
5155     
5156 });
5157
5158
5159
5160  
5161
5162  
5163        /*
5164  * - LGPL
5165  *
5166  * navbar
5167  * navbar-fixed-top
5168  * navbar-expand-md  fixed-top 
5169  */
5170
5171 /**
5172  * @class Roo.bootstrap.NavHeaderbar
5173  * @extends Roo.bootstrap.NavSimplebar
5174  * Bootstrap Sidebar class
5175  *
5176  * @cfg {String} brand what is brand
5177  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5178  * @cfg {String} brand_href href of the brand
5179  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5180  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5181  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5182  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5183  * 
5184  * @constructor
5185  * Create a new Sidebar
5186  * @param {Object} config The config object
5187  */
5188
5189
5190 Roo.bootstrap.NavHeaderbar = function(config){
5191     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5192       
5193 };
5194
5195 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5196     
5197     position: '',
5198     brand: '',
5199     brand_href: false,
5200     srButton : true,
5201     autohide : false,
5202     desktopCenter : false,
5203    
5204     
5205     getAutoCreate : function(){
5206         
5207         var   cfg = {
5208             tag: this.nav || 'nav',
5209             cls: 'navbar navbar-expand-md',
5210             role: 'navigation',
5211             cn: []
5212         };
5213         
5214         var cn = cfg.cn;
5215         if (this.desktopCenter) {
5216             cn.push({cls : 'container', cn : []});
5217             cn = cn[0].cn;
5218         }
5219         
5220         if(this.srButton){
5221             var btn = {
5222                 tag: 'button',
5223                 type: 'button',
5224                 cls: 'navbar-toggle navbar-toggler',
5225                 'data-toggle': 'collapse',
5226                 cn: [
5227                     {
5228                         tag: 'span',
5229                         cls: 'sr-only',
5230                         html: 'Toggle navigation'
5231                     },
5232                     {
5233                         tag: 'span',
5234                         cls: 'icon-bar navbar-toggler-icon'
5235                     },
5236                     {
5237                         tag: 'span',
5238                         cls: 'icon-bar'
5239                     },
5240                     {
5241                         tag: 'span',
5242                         cls: 'icon-bar'
5243                     }
5244                 ]
5245             };
5246             
5247             cn.push( Roo.bootstrap.version == 4 ? btn : {
5248                 tag: 'div',
5249                 cls: 'navbar-header',
5250                 cn: [
5251                     btn
5252                 ]
5253             });
5254         }
5255         
5256         cn.push({
5257             tag: 'div',
5258             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5259             cn : []
5260         });
5261         
5262         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5263         
5264         if (['light','white'].indexOf(this.weight) > -1) {
5265             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5266         }
5267         cfg.cls += ' bg-' + this.weight;
5268         
5269         
5270         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5271             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5272             
5273             // tag can override this..
5274             
5275             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5276         }
5277         
5278         if (this.brand !== '') {
5279             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5280             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5281                 tag: 'a',
5282                 href: this.brand_href ? this.brand_href : '#',
5283                 cls: 'navbar-brand',
5284                 cn: [
5285                 this.brand
5286                 ]
5287             });
5288         }
5289         
5290         if(this.main){
5291             cfg.cls += ' main-nav';
5292         }
5293         
5294         
5295         return cfg;
5296
5297         
5298     },
5299     getHeaderChildContainer : function()
5300     {
5301         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5302             return this.el.select('.navbar-header',true).first();
5303         }
5304         
5305         return this.getChildContainer();
5306     },
5307     
5308     getChildContainer : function()
5309     {
5310          
5311         return this.el.select('.roo-navbar-collapse',true).first();
5312          
5313         
5314     },
5315     
5316     initEvents : function()
5317     {
5318         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5319         
5320         if (this.autohide) {
5321             
5322             var prevScroll = 0;
5323             var ft = this.el;
5324             
5325             Roo.get(document).on('scroll',function(e) {
5326                 var ns = Roo.get(document).getScroll().top;
5327                 var os = prevScroll;
5328                 prevScroll = ns;
5329                 
5330                 if(ns > os){
5331                     ft.removeClass('slideDown');
5332                     ft.addClass('slideUp');
5333                     return;
5334                 }
5335                 ft.removeClass('slideUp');
5336                 ft.addClass('slideDown');
5337                  
5338               
5339           },this);
5340         }
5341     }    
5342     
5343 });
5344
5345
5346
5347  
5348
5349  /*
5350  * - LGPL
5351  *
5352  * navbar
5353  * 
5354  */
5355
5356 /**
5357  * @class Roo.bootstrap.NavSidebar
5358  * @extends Roo.bootstrap.Navbar
5359  * Bootstrap Sidebar class
5360  * 
5361  * @constructor
5362  * Create a new Sidebar
5363  * @param {Object} config The config object
5364  */
5365
5366
5367 Roo.bootstrap.NavSidebar = function(config){
5368     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5369 };
5370
5371 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5372     
5373     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5374     
5375     getAutoCreate : function(){
5376         
5377         
5378         return  {
5379             tag: 'div',
5380             cls: 'sidebar sidebar-nav'
5381         };
5382     
5383         
5384     }
5385     
5386     
5387     
5388 });
5389
5390
5391
5392  
5393
5394  /*
5395  * - LGPL
5396  *
5397  * nav group
5398  * 
5399  */
5400
5401 /**
5402  * @class Roo.bootstrap.NavGroup
5403  * @extends Roo.bootstrap.Component
5404  * Bootstrap NavGroup class
5405  * @cfg {String} align (left|right)
5406  * @cfg {Boolean} inverse
5407  * @cfg {String} type (nav|pills|tab) default nav
5408  * @cfg {String} navId - reference Id for navbar.
5409
5410  * 
5411  * @constructor
5412  * Create a new nav group
5413  * @param {Object} config The config object
5414  */
5415
5416 Roo.bootstrap.NavGroup = function(config){
5417     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5418     this.navItems = [];
5419    
5420     Roo.bootstrap.NavGroup.register(this);
5421      this.addEvents({
5422         /**
5423              * @event changed
5424              * Fires when the active item changes
5425              * @param {Roo.bootstrap.NavGroup} this
5426              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5427              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5428          */
5429         'changed': true
5430      });
5431     
5432 };
5433
5434 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5435     
5436     align: '',
5437     inverse: false,
5438     form: false,
5439     type: 'nav',
5440     navId : '',
5441     // private
5442     
5443     navItems : false, 
5444     
5445     getAutoCreate : function()
5446     {
5447         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5448         
5449         cfg = {
5450             tag : 'ul',
5451             cls: 'nav' 
5452         };
5453         if (Roo.bootstrap.version == 4) {
5454             if (['tabs','pills'].indexOf(this.type) != -1) {
5455                 cfg.cls += ' nav-' + this.type; 
5456             } else {
5457                 // trying to remove so header bar can right align top?
5458                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5459                     // do not use on header bar... 
5460                     cfg.cls += ' navbar-nav';
5461                 }
5462             }
5463             
5464         } else {
5465             if (['tabs','pills'].indexOf(this.type) != -1) {
5466                 cfg.cls += ' nav-' + this.type
5467             } else {
5468                 if (this.type !== 'nav') {
5469                     Roo.log('nav type must be nav/tabs/pills')
5470                 }
5471                 cfg.cls += ' navbar-nav'
5472             }
5473         }
5474         
5475         if (this.parent() && this.parent().sidebar) {
5476             cfg = {
5477                 tag: 'ul',
5478                 cls: 'dashboard-menu sidebar-menu'
5479             };
5480             
5481             return cfg;
5482         }
5483         
5484         if (this.form === true) {
5485             cfg = {
5486                 tag: 'form',
5487                 cls: 'navbar-form form-inline'
5488             };
5489             //nav navbar-right ml-md-auto
5490             if (this.align === 'right') {
5491                 cfg.cls += ' navbar-right ml-md-auto';
5492             } else {
5493                 cfg.cls += ' navbar-left';
5494             }
5495         }
5496         
5497         if (this.align === 'right') {
5498             cfg.cls += ' navbar-right ml-md-auto';
5499         } else {
5500             cfg.cls += ' mr-auto';
5501         }
5502         
5503         if (this.inverse) {
5504             cfg.cls += ' navbar-inverse';
5505             
5506         }
5507         
5508         
5509         return cfg;
5510     },
5511     /**
5512     * sets the active Navigation item
5513     * @param {Roo.bootstrap.NavItem} the new current navitem
5514     */
5515     setActiveItem : function(item)
5516     {
5517         var prev = false;
5518         Roo.each(this.navItems, function(v){
5519             if (v == item) {
5520                 return ;
5521             }
5522             if (v.isActive()) {
5523                 v.setActive(false, true);
5524                 prev = v;
5525                 
5526             }
5527             
5528         });
5529
5530         item.setActive(true, true);
5531         this.fireEvent('changed', this, item, prev);
5532         
5533         
5534     },
5535     /**
5536     * gets the active Navigation item
5537     * @return {Roo.bootstrap.NavItem} the current navitem
5538     */
5539     getActive : function()
5540     {
5541         
5542         var prev = false;
5543         Roo.each(this.navItems, function(v){
5544             
5545             if (v.isActive()) {
5546                 prev = v;
5547                 
5548             }
5549             
5550         });
5551         return prev;
5552     },
5553     
5554     indexOfNav : function()
5555     {
5556         
5557         var prev = false;
5558         Roo.each(this.navItems, function(v,i){
5559             
5560             if (v.isActive()) {
5561                 prev = i;
5562                 
5563             }
5564             
5565         });
5566         return prev;
5567     },
5568     /**
5569     * adds a Navigation item
5570     * @param {Roo.bootstrap.NavItem} the navitem to add
5571     */
5572     addItem : function(cfg)
5573     {
5574         if (this.form && Roo.bootstrap.version == 4) {
5575             cfg.tag = 'div';
5576         }
5577         var cn = new Roo.bootstrap.NavItem(cfg);
5578         this.register(cn);
5579         cn.parentId = this.id;
5580         cn.onRender(this.el, null);
5581         return cn;
5582     },
5583     /**
5584     * register a Navigation item
5585     * @param {Roo.bootstrap.NavItem} the navitem to add
5586     */
5587     register : function(item)
5588     {
5589         this.navItems.push( item);
5590         item.navId = this.navId;
5591     
5592     },
5593     
5594     /**
5595     * clear all the Navigation item
5596     */
5597    
5598     clearAll : function()
5599     {
5600         this.navItems = [];
5601         this.el.dom.innerHTML = '';
5602     },
5603     
5604     getNavItem: function(tabId)
5605     {
5606         var ret = false;
5607         Roo.each(this.navItems, function(e) {
5608             if (e.tabId == tabId) {
5609                ret =  e;
5610                return false;
5611             }
5612             return true;
5613             
5614         });
5615         return ret;
5616     },
5617     
5618     setActiveNext : function()
5619     {
5620         var i = this.indexOfNav(this.getActive());
5621         if (i > this.navItems.length) {
5622             return;
5623         }
5624         this.setActiveItem(this.navItems[i+1]);
5625     },
5626     setActivePrev : function()
5627     {
5628         var i = this.indexOfNav(this.getActive());
5629         if (i  < 1) {
5630             return;
5631         }
5632         this.setActiveItem(this.navItems[i-1]);
5633     },
5634     clearWasActive : function(except) {
5635         Roo.each(this.navItems, function(e) {
5636             if (e.tabId != except.tabId && e.was_active) {
5637                e.was_active = false;
5638                return false;
5639             }
5640             return true;
5641             
5642         });
5643     },
5644     getWasActive : function ()
5645     {
5646         var r = false;
5647         Roo.each(this.navItems, function(e) {
5648             if (e.was_active) {
5649                r = e;
5650                return false;
5651             }
5652             return true;
5653             
5654         });
5655         return r;
5656     }
5657     
5658     
5659 });
5660
5661  
5662 Roo.apply(Roo.bootstrap.NavGroup, {
5663     
5664     groups: {},
5665      /**
5666     * register a Navigation Group
5667     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5668     */
5669     register : function(navgrp)
5670     {
5671         this.groups[navgrp.navId] = navgrp;
5672         
5673     },
5674     /**
5675     * fetch a Navigation Group based on the navigation ID
5676     * @param {string} the navgroup to add
5677     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5678     */
5679     get: function(navId) {
5680         if (typeof(this.groups[navId]) == 'undefined') {
5681             return false;
5682             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5683         }
5684         return this.groups[navId] ;
5685     }
5686     
5687     
5688     
5689 });
5690
5691  /*
5692  * - LGPL
5693  *
5694  * row
5695  * 
5696  */
5697
5698 /**
5699  * @class Roo.bootstrap.NavItem
5700  * @extends Roo.bootstrap.Component
5701  * Bootstrap Navbar.NavItem class
5702  * @cfg {String} href  link to
5703  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5704
5705  * @cfg {String} html content of button
5706  * @cfg {String} badge text inside badge
5707  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5708  * @cfg {String} glyphicon DEPRICATED - use fa
5709  * @cfg {String} icon DEPRICATED - use fa
5710  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5711  * @cfg {Boolean} active Is item active
5712  * @cfg {Boolean} disabled Is item disabled
5713  
5714  * @cfg {Boolean} preventDefault (true | false) default false
5715  * @cfg {String} tabId the tab that this item activates.
5716  * @cfg {String} tagtype (a|span) render as a href or span?
5717  * @cfg {Boolean} animateRef (true|false) link to element default false  
5718   
5719  * @constructor
5720  * Create a new Navbar Item
5721  * @param {Object} config The config object
5722  */
5723 Roo.bootstrap.NavItem = function(config){
5724     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5725     this.addEvents({
5726         // raw events
5727         /**
5728          * @event click
5729          * The raw click event for the entire grid.
5730          * @param {Roo.EventObject} e
5731          */
5732         "click" : true,
5733          /**
5734             * @event changed
5735             * Fires when the active item active state changes
5736             * @param {Roo.bootstrap.NavItem} this
5737             * @param {boolean} state the new state
5738              
5739          */
5740         'changed': true,
5741         /**
5742             * @event scrollto
5743             * Fires when scroll to element
5744             * @param {Roo.bootstrap.NavItem} this
5745             * @param {Object} options
5746             * @param {Roo.EventObject} e
5747              
5748          */
5749         'scrollto': true
5750     });
5751    
5752 };
5753
5754 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5755     
5756     href: false,
5757     html: '',
5758     badge: '',
5759     icon: false,
5760     fa : false,
5761     glyphicon: false,
5762     active: false,
5763     preventDefault : false,
5764     tabId : false,
5765     tagtype : 'a',
5766     tag: 'li',
5767     disabled : false,
5768     animateRef : false,
5769     was_active : false,
5770     button_weight : '',
5771     button_outline : false,
5772     
5773     navLink: false,
5774     
5775     getAutoCreate : function(){
5776          
5777         var cfg = {
5778             tag: this.tag,
5779             cls: 'nav-item'
5780         };
5781         
5782         if (this.active) {
5783             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5784         }
5785         if (this.disabled) {
5786             cfg.cls += ' disabled';
5787         }
5788         
5789         // BS4 only?
5790         if (this.button_weight.length) {
5791             cfg.tag = this.href ? 'a' : 'button';
5792             cfg.html = this.html || '';
5793             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5794             if (this.href) {
5795                 cfg.href = this.href;
5796             }
5797             if (this.fa) {
5798                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5799             }
5800             
5801             // menu .. should add dropdown-menu class - so no need for carat..
5802             
5803             if (this.badge !== '') {
5804                  
5805                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5806             }
5807             return cfg;
5808         }
5809         
5810         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5811             cfg.cn = [
5812                 {
5813                     tag: this.tagtype,
5814                     href : this.href || "#",
5815                     html: this.html || ''
5816                 }
5817             ];
5818             if (this.tagtype == 'a') {
5819                 cfg.cn[0].cls = 'nav-link';
5820             }
5821             if (this.icon) {
5822                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5823             }
5824             if (this.fa) {
5825                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5826             }
5827             if(this.glyphicon) {
5828                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5829             }
5830             
5831             if (this.menu) {
5832                 
5833                 cfg.cn[0].html += " <span class='caret'></span>";
5834              
5835             }
5836             
5837             if (this.badge !== '') {
5838                  
5839                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5840             }
5841         }
5842         
5843         
5844         
5845         return cfg;
5846     },
5847     onRender : function(ct, position)
5848     {
5849        // Roo.log("Call onRender: " + this.xtype);
5850         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5851             this.tag = 'div';
5852         }
5853         
5854         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5855         this.navLink = this.el.select('.nav-link',true).first();
5856         return ret;
5857     },
5858       
5859     
5860     initEvents: function() 
5861     {
5862         if (typeof (this.menu) != 'undefined') {
5863             this.menu.parentType = this.xtype;
5864             this.menu.triggerEl = this.el;
5865             this.menu = this.addxtype(Roo.apply({}, this.menu));
5866         }
5867         
5868         this.el.select('a',true).on('click', this.onClick, this);
5869         
5870         if(this.tagtype == 'span'){
5871             this.el.select('span',true).on('click', this.onClick, this);
5872         }
5873        
5874         // at this point parent should be available..
5875         this.parent().register(this);
5876     },
5877     
5878     onClick : function(e)
5879     {
5880         if (e.getTarget('.dropdown-menu-item')) {
5881             // did you click on a menu itemm.... - then don't trigger onclick..
5882             return;
5883         }
5884         
5885         if(
5886                 this.preventDefault || 
5887                 this.href == '#' 
5888         ){
5889             Roo.log("NavItem - prevent Default?");
5890             e.preventDefault();
5891         }
5892         
5893         if (this.disabled) {
5894             return;
5895         }
5896         
5897         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5898         if (tg && tg.transition) {
5899             Roo.log("waiting for the transitionend");
5900             return;
5901         }
5902         
5903         
5904         
5905         //Roo.log("fire event clicked");
5906         if(this.fireEvent('click', this, e) === false){
5907             return;
5908         };
5909         
5910         if(this.tagtype == 'span'){
5911             return;
5912         }
5913         
5914         //Roo.log(this.href);
5915         var ael = this.el.select('a',true).first();
5916         //Roo.log(ael);
5917         
5918         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5919             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5920             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5921                 return; // ignore... - it's a 'hash' to another page.
5922             }
5923             Roo.log("NavItem - prevent Default?");
5924             e.preventDefault();
5925             this.scrollToElement(e);
5926         }
5927         
5928         
5929         var p =  this.parent();
5930    
5931         if (['tabs','pills'].indexOf(p.type)!==-1) {
5932             if (typeof(p.setActiveItem) !== 'undefined') {
5933                 p.setActiveItem(this);
5934             }
5935         }
5936         
5937         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5938         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5939             // remove the collapsed menu expand...
5940             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5941         }
5942     },
5943     
5944     isActive: function () {
5945         return this.active
5946     },
5947     setActive : function(state, fire, is_was_active)
5948     {
5949         if (this.active && !state && this.navId) {
5950             this.was_active = true;
5951             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5952             if (nv) {
5953                 nv.clearWasActive(this);
5954             }
5955             
5956         }
5957         this.active = state;
5958         
5959         if (!state ) {
5960             this.el.removeClass('active');
5961             this.navLink ? this.navLink.removeClass('active') : false;
5962         } else if (!this.el.hasClass('active')) {
5963             
5964             this.el.addClass('active');
5965             if (Roo.bootstrap.version == 4 && this.navLink ) {
5966                 this.navLink.addClass('active');
5967             }
5968             
5969         }
5970         if (fire) {
5971             this.fireEvent('changed', this, state);
5972         }
5973         
5974         // show a panel if it's registered and related..
5975         
5976         if (!this.navId || !this.tabId || !state || is_was_active) {
5977             return;
5978         }
5979         
5980         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5981         if (!tg) {
5982             return;
5983         }
5984         var pan = tg.getPanelByName(this.tabId);
5985         if (!pan) {
5986             return;
5987         }
5988         // if we can not flip to new panel - go back to old nav highlight..
5989         if (false == tg.showPanel(pan)) {
5990             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5991             if (nv) {
5992                 var onav = nv.getWasActive();
5993                 if (onav) {
5994                     onav.setActive(true, false, true);
5995                 }
5996             }
5997             
5998         }
5999         
6000         
6001         
6002     },
6003      // this should not be here...
6004     setDisabled : function(state)
6005     {
6006         this.disabled = state;
6007         if (!state ) {
6008             this.el.removeClass('disabled');
6009         } else if (!this.el.hasClass('disabled')) {
6010             this.el.addClass('disabled');
6011         }
6012         
6013     },
6014     
6015     /**
6016      * Fetch the element to display the tooltip on.
6017      * @return {Roo.Element} defaults to this.el
6018      */
6019     tooltipEl : function()
6020     {
6021         return this.el.select('' + this.tagtype + '', true).first();
6022     },
6023     
6024     scrollToElement : function(e)
6025     {
6026         var c = document.body;
6027         
6028         /*
6029          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6030          */
6031         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6032             c = document.documentElement;
6033         }
6034         
6035         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6036         
6037         if(!target){
6038             return;
6039         }
6040
6041         var o = target.calcOffsetsTo(c);
6042         
6043         var options = {
6044             target : target,
6045             value : o[1]
6046         };
6047         
6048         this.fireEvent('scrollto', this, options, e);
6049         
6050         Roo.get(c).scrollTo('top', options.value, true);
6051         
6052         return;
6053     }
6054 });
6055  
6056
6057  /*
6058  * - LGPL
6059  *
6060  * sidebar item
6061  *
6062  *  li
6063  *    <span> icon </span>
6064  *    <span> text </span>
6065  *    <span>badge </span>
6066  */
6067
6068 /**
6069  * @class Roo.bootstrap.NavSidebarItem
6070  * @extends Roo.bootstrap.NavItem
6071  * Bootstrap Navbar.NavSidebarItem class
6072  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6073  * {Boolean} open is the menu open
6074  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6075  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6076  * {String} buttonSize (sm|md|lg)the extra classes for the button
6077  * {Boolean} showArrow show arrow next to the text (default true)
6078  * @constructor
6079  * Create a new Navbar Button
6080  * @param {Object} config The config object
6081  */
6082 Roo.bootstrap.NavSidebarItem = function(config){
6083     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6084     this.addEvents({
6085         // raw events
6086         /**
6087          * @event click
6088          * The raw click event for the entire grid.
6089          * @param {Roo.EventObject} e
6090          */
6091         "click" : true,
6092          /**
6093             * @event changed
6094             * Fires when the active item active state changes
6095             * @param {Roo.bootstrap.NavSidebarItem} this
6096             * @param {boolean} state the new state
6097              
6098          */
6099         'changed': true
6100     });
6101    
6102 };
6103
6104 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6105     
6106     badgeWeight : 'default',
6107     
6108     open: false,
6109     
6110     buttonView : false,
6111     
6112     buttonWeight : 'default',
6113     
6114     buttonSize : 'md',
6115     
6116     showArrow : true,
6117     
6118     getAutoCreate : function(){
6119         
6120         
6121         var a = {
6122                 tag: 'a',
6123                 href : this.href || '#',
6124                 cls: '',
6125                 html : '',
6126                 cn : []
6127         };
6128         
6129         if(this.buttonView){
6130             a = {
6131                 tag: 'button',
6132                 href : this.href || '#',
6133                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6134                 html : this.html,
6135                 cn : []
6136             };
6137         }
6138         
6139         var cfg = {
6140             tag: 'li',
6141             cls: '',
6142             cn: [ a ]
6143         };
6144         
6145         if (this.active) {
6146             cfg.cls += ' active';
6147         }
6148         
6149         if (this.disabled) {
6150             cfg.cls += ' disabled';
6151         }
6152         if (this.open) {
6153             cfg.cls += ' open x-open';
6154         }
6155         // left icon..
6156         if (this.glyphicon || this.icon) {
6157             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6158             a.cn.push({ tag : 'i', cls : c }) ;
6159         }
6160         
6161         if(!this.buttonView){
6162             var span = {
6163                 tag: 'span',
6164                 html : this.html || ''
6165             };
6166
6167             a.cn.push(span);
6168             
6169         }
6170         
6171         if (this.badge !== '') {
6172             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6173         }
6174         
6175         if (this.menu) {
6176             
6177             if(this.showArrow){
6178                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6179             }
6180             
6181             a.cls += ' dropdown-toggle treeview' ;
6182         }
6183         
6184         return cfg;
6185     },
6186     
6187     initEvents : function()
6188     { 
6189         if (typeof (this.menu) != 'undefined') {
6190             this.menu.parentType = this.xtype;
6191             this.menu.triggerEl = this.el;
6192             this.menu = this.addxtype(Roo.apply({}, this.menu));
6193         }
6194         
6195         this.el.on('click', this.onClick, this);
6196         
6197         if(this.badge !== ''){
6198             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6199         }
6200         
6201     },
6202     
6203     onClick : function(e)
6204     {
6205         if(this.disabled){
6206             e.preventDefault();
6207             return;
6208         }
6209         
6210         if(this.preventDefault){
6211             e.preventDefault();
6212         }
6213         
6214         this.fireEvent('click', this, e);
6215     },
6216     
6217     disable : function()
6218     {
6219         this.setDisabled(true);
6220     },
6221     
6222     enable : function()
6223     {
6224         this.setDisabled(false);
6225     },
6226     
6227     setDisabled : function(state)
6228     {
6229         if(this.disabled == state){
6230             return;
6231         }
6232         
6233         this.disabled = state;
6234         
6235         if (state) {
6236             this.el.addClass('disabled');
6237             return;
6238         }
6239         
6240         this.el.removeClass('disabled');
6241         
6242         return;
6243     },
6244     
6245     setActive : function(state)
6246     {
6247         if(this.active == state){
6248             return;
6249         }
6250         
6251         this.active = state;
6252         
6253         if (state) {
6254             this.el.addClass('active');
6255             return;
6256         }
6257         
6258         this.el.removeClass('active');
6259         
6260         return;
6261     },
6262     
6263     isActive: function () 
6264     {
6265         return this.active;
6266     },
6267     
6268     setBadge : function(str)
6269     {
6270         if(!this.badgeEl){
6271             return;
6272         }
6273         
6274         this.badgeEl.dom.innerHTML = str;
6275     }
6276     
6277    
6278      
6279  
6280 });
6281  
6282
6283  /*
6284  * - LGPL
6285  *
6286  * row
6287  * 
6288  */
6289
6290 /**
6291  * @class Roo.bootstrap.Row
6292  * @extends Roo.bootstrap.Component
6293  * Bootstrap Row class (contains columns...)
6294  * 
6295  * @constructor
6296  * Create a new Row
6297  * @param {Object} config The config object
6298  */
6299
6300 Roo.bootstrap.Row = function(config){
6301     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6302 };
6303
6304 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6305     
6306     getAutoCreate : function(){
6307        return {
6308             cls: 'row clearfix'
6309        };
6310     }
6311     
6312     
6313 });
6314
6315  
6316
6317  /*
6318  * - LGPL
6319  *
6320  * pagination
6321  * 
6322  */
6323
6324 /**
6325  * @class Roo.bootstrap.Pagination
6326  * @extends Roo.bootstrap.Component
6327  * Bootstrap Pagination class
6328  * @cfg {String} size xs | sm | md | lg
6329  * @cfg {Boolean} inverse false | true
6330  * 
6331  * @constructor
6332  * Create a new Pagination
6333  * @param {Object} config The config object
6334  */
6335
6336 Roo.bootstrap.Pagination = function(config){
6337     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6338 };
6339
6340 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6341     
6342     cls: false,
6343     size: false,
6344     inverse: false,
6345     
6346     getAutoCreate : function(){
6347         var cfg = {
6348             tag: 'ul',
6349                 cls: 'pagination'
6350         };
6351         if (this.inverse) {
6352             cfg.cls += ' inverse';
6353         }
6354         if (this.html) {
6355             cfg.html=this.html;
6356         }
6357         if (this.cls) {
6358             cfg.cls += " " + this.cls;
6359         }
6360         return cfg;
6361     }
6362    
6363 });
6364
6365  
6366
6367  /*
6368  * - LGPL
6369  *
6370  * Pagination item
6371  * 
6372  */
6373
6374
6375 /**
6376  * @class Roo.bootstrap.PaginationItem
6377  * @extends Roo.bootstrap.Component
6378  * Bootstrap PaginationItem class
6379  * @cfg {String} html text
6380  * @cfg {String} href the link
6381  * @cfg {Boolean} preventDefault (true | false) default true
6382  * @cfg {Boolean} active (true | false) default false
6383  * @cfg {Boolean} disabled default false
6384  * 
6385  * 
6386  * @constructor
6387  * Create a new PaginationItem
6388  * @param {Object} config The config object
6389  */
6390
6391
6392 Roo.bootstrap.PaginationItem = function(config){
6393     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6394     this.addEvents({
6395         // raw events
6396         /**
6397          * @event click
6398          * The raw click event for the entire grid.
6399          * @param {Roo.EventObject} e
6400          */
6401         "click" : true
6402     });
6403 };
6404
6405 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6406     
6407     href : false,
6408     html : false,
6409     preventDefault: true,
6410     active : false,
6411     cls : false,
6412     disabled: false,
6413     
6414     getAutoCreate : function(){
6415         var cfg= {
6416             tag: 'li',
6417             cn: [
6418                 {
6419                     tag : 'a',
6420                     href : this.href ? this.href : '#',
6421                     html : this.html ? this.html : ''
6422                 }
6423             ]
6424         };
6425         
6426         if(this.cls){
6427             cfg.cls = this.cls;
6428         }
6429         
6430         if(this.disabled){
6431             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6432         }
6433         
6434         if(this.active){
6435             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6436         }
6437         
6438         return cfg;
6439     },
6440     
6441     initEvents: function() {
6442         
6443         this.el.on('click', this.onClick, this);
6444         
6445     },
6446     onClick : function(e)
6447     {
6448         Roo.log('PaginationItem on click ');
6449         if(this.preventDefault){
6450             e.preventDefault();
6451         }
6452         
6453         if(this.disabled){
6454             return;
6455         }
6456         
6457         this.fireEvent('click', this, e);
6458     }
6459    
6460 });
6461
6462  
6463
6464  /*
6465  * - LGPL
6466  *
6467  * slider
6468  * 
6469  */
6470
6471
6472 /**
6473  * @class Roo.bootstrap.Slider
6474  * @extends Roo.bootstrap.Component
6475  * Bootstrap Slider class
6476  *    
6477  * @constructor
6478  * Create a new Slider
6479  * @param {Object} config The config object
6480  */
6481
6482 Roo.bootstrap.Slider = function(config){
6483     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6484 };
6485
6486 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6487     
6488     getAutoCreate : function(){
6489         
6490         var cfg = {
6491             tag: 'div',
6492             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6493             cn: [
6494                 {
6495                     tag: 'a',
6496                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6497                 }
6498             ]
6499         };
6500         
6501         return cfg;
6502     }
6503    
6504 });
6505
6506  /*
6507  * Based on:
6508  * Ext JS Library 1.1.1
6509  * Copyright(c) 2006-2007, Ext JS, LLC.
6510  *
6511  * Originally Released Under LGPL - original licence link has changed is not relivant.
6512  *
6513  * Fork - LGPL
6514  * <script type="text/javascript">
6515  */
6516  
6517
6518 /**
6519  * @class Roo.grid.ColumnModel
6520  * @extends Roo.util.Observable
6521  * This is the default implementation of a ColumnModel used by the Grid. It defines
6522  * the columns in the grid.
6523  * <br>Usage:<br>
6524  <pre><code>
6525  var colModel = new Roo.grid.ColumnModel([
6526         {header: "Ticker", width: 60, sortable: true, locked: true},
6527         {header: "Company Name", width: 150, sortable: true},
6528         {header: "Market Cap.", width: 100, sortable: true},
6529         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6530         {header: "Employees", width: 100, sortable: true, resizable: false}
6531  ]);
6532  </code></pre>
6533  * <p>
6534  
6535  * The config options listed for this class are options which may appear in each
6536  * individual column definition.
6537  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6538  * @constructor
6539  * @param {Object} config An Array of column config objects. See this class's
6540  * config objects for details.
6541 */
6542 Roo.grid.ColumnModel = function(config){
6543         /**
6544      * The config passed into the constructor
6545      */
6546     this.config = config;
6547     this.lookup = {};
6548
6549     // if no id, create one
6550     // if the column does not have a dataIndex mapping,
6551     // map it to the order it is in the config
6552     for(var i = 0, len = config.length; i < len; i++){
6553         var c = config[i];
6554         if(typeof c.dataIndex == "undefined"){
6555             c.dataIndex = i;
6556         }
6557         if(typeof c.renderer == "string"){
6558             c.renderer = Roo.util.Format[c.renderer];
6559         }
6560         if(typeof c.id == "undefined"){
6561             c.id = Roo.id();
6562         }
6563         if(c.editor && c.editor.xtype){
6564             c.editor  = Roo.factory(c.editor, Roo.grid);
6565         }
6566         if(c.editor && c.editor.isFormField){
6567             c.editor = new Roo.grid.GridEditor(c.editor);
6568         }
6569         this.lookup[c.id] = c;
6570     }
6571
6572     /**
6573      * The width of columns which have no width specified (defaults to 100)
6574      * @type Number
6575      */
6576     this.defaultWidth = 100;
6577
6578     /**
6579      * Default sortable of columns which have no sortable specified (defaults to false)
6580      * @type Boolean
6581      */
6582     this.defaultSortable = false;
6583
6584     this.addEvents({
6585         /**
6586              * @event widthchange
6587              * Fires when the width of a column changes.
6588              * @param {ColumnModel} this
6589              * @param {Number} columnIndex The column index
6590              * @param {Number} newWidth The new width
6591              */
6592             "widthchange": true,
6593         /**
6594              * @event headerchange
6595              * Fires when the text of a header changes.
6596              * @param {ColumnModel} this
6597              * @param {Number} columnIndex The column index
6598              * @param {Number} newText The new header text
6599              */
6600             "headerchange": true,
6601         /**
6602              * @event hiddenchange
6603              * Fires when a column is hidden or "unhidden".
6604              * @param {ColumnModel} this
6605              * @param {Number} columnIndex The column index
6606              * @param {Boolean} hidden true if hidden, false otherwise
6607              */
6608             "hiddenchange": true,
6609             /**
6610          * @event columnmoved
6611          * Fires when a column is moved.
6612          * @param {ColumnModel} this
6613          * @param {Number} oldIndex
6614          * @param {Number} newIndex
6615          */
6616         "columnmoved" : true,
6617         /**
6618          * @event columlockchange
6619          * Fires when a column's locked state is changed
6620          * @param {ColumnModel} this
6621          * @param {Number} colIndex
6622          * @param {Boolean} locked true if locked
6623          */
6624         "columnlockchange" : true
6625     });
6626     Roo.grid.ColumnModel.superclass.constructor.call(this);
6627 };
6628 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6629     /**
6630      * @cfg {String} header The header text to display in the Grid view.
6631      */
6632     /**
6633      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6634      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6635      * specified, the column's index is used as an index into the Record's data Array.
6636      */
6637     /**
6638      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6639      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6640      */
6641     /**
6642      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6643      * Defaults to the value of the {@link #defaultSortable} property.
6644      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6645      */
6646     /**
6647      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6648      */
6649     /**
6650      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6651      */
6652     /**
6653      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6654      */
6655     /**
6656      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6657      */
6658     /**
6659      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6660      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6661      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6662      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6663      */
6664        /**
6665      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6666      */
6667     /**
6668      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6669      */
6670     /**
6671      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6672      */
6673     /**
6674      * @cfg {String} cursor (Optional)
6675      */
6676     /**
6677      * @cfg {String} tooltip (Optional)
6678      */
6679     /**
6680      * @cfg {Number} xs (Optional)
6681      */
6682     /**
6683      * @cfg {Number} sm (Optional)
6684      */
6685     /**
6686      * @cfg {Number} md (Optional)
6687      */
6688     /**
6689      * @cfg {Number} lg (Optional)
6690      */
6691     /**
6692      * Returns the id of the column at the specified index.
6693      * @param {Number} index The column index
6694      * @return {String} the id
6695      */
6696     getColumnId : function(index){
6697         return this.config[index].id;
6698     },
6699
6700     /**
6701      * Returns the column for a specified id.
6702      * @param {String} id The column id
6703      * @return {Object} the column
6704      */
6705     getColumnById : function(id){
6706         return this.lookup[id];
6707     },
6708
6709     
6710     /**
6711      * Returns the column for a specified dataIndex.
6712      * @param {String} dataIndex The column dataIndex
6713      * @return {Object|Boolean} the column or false if not found
6714      */
6715     getColumnByDataIndex: function(dataIndex){
6716         var index = this.findColumnIndex(dataIndex);
6717         return index > -1 ? this.config[index] : false;
6718     },
6719     
6720     /**
6721      * Returns the index for a specified column id.
6722      * @param {String} id The column id
6723      * @return {Number} the index, or -1 if not found
6724      */
6725     getIndexById : function(id){
6726         for(var i = 0, len = this.config.length; i < len; i++){
6727             if(this.config[i].id == id){
6728                 return i;
6729             }
6730         }
6731         return -1;
6732     },
6733     
6734     /**
6735      * Returns the index for a specified column dataIndex.
6736      * @param {String} dataIndex The column dataIndex
6737      * @return {Number} the index, or -1 if not found
6738      */
6739     
6740     findColumnIndex : function(dataIndex){
6741         for(var i = 0, len = this.config.length; i < len; i++){
6742             if(this.config[i].dataIndex == dataIndex){
6743                 return i;
6744             }
6745         }
6746         return -1;
6747     },
6748     
6749     
6750     moveColumn : function(oldIndex, newIndex){
6751         var c = this.config[oldIndex];
6752         this.config.splice(oldIndex, 1);
6753         this.config.splice(newIndex, 0, c);
6754         this.dataMap = null;
6755         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6756     },
6757
6758     isLocked : function(colIndex){
6759         return this.config[colIndex].locked === true;
6760     },
6761
6762     setLocked : function(colIndex, value, suppressEvent){
6763         if(this.isLocked(colIndex) == value){
6764             return;
6765         }
6766         this.config[colIndex].locked = value;
6767         if(!suppressEvent){
6768             this.fireEvent("columnlockchange", this, colIndex, value);
6769         }
6770     },
6771
6772     getTotalLockedWidth : function(){
6773         var totalWidth = 0;
6774         for(var i = 0; i < this.config.length; i++){
6775             if(this.isLocked(i) && !this.isHidden(i)){
6776                 this.totalWidth += this.getColumnWidth(i);
6777             }
6778         }
6779         return totalWidth;
6780     },
6781
6782     getLockedCount : function(){
6783         for(var i = 0, len = this.config.length; i < len; i++){
6784             if(!this.isLocked(i)){
6785                 return i;
6786             }
6787         }
6788         
6789         return this.config.length;
6790     },
6791
6792     /**
6793      * Returns the number of columns.
6794      * @return {Number}
6795      */
6796     getColumnCount : function(visibleOnly){
6797         if(visibleOnly === true){
6798             var c = 0;
6799             for(var i = 0, len = this.config.length; i < len; i++){
6800                 if(!this.isHidden(i)){
6801                     c++;
6802                 }
6803             }
6804             return c;
6805         }
6806         return this.config.length;
6807     },
6808
6809     /**
6810      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6811      * @param {Function} fn
6812      * @param {Object} scope (optional)
6813      * @return {Array} result
6814      */
6815     getColumnsBy : function(fn, scope){
6816         var r = [];
6817         for(var i = 0, len = this.config.length; i < len; i++){
6818             var c = this.config[i];
6819             if(fn.call(scope||this, c, i) === true){
6820                 r[r.length] = c;
6821             }
6822         }
6823         return r;
6824     },
6825
6826     /**
6827      * Returns true if the specified column is sortable.
6828      * @param {Number} col The column index
6829      * @return {Boolean}
6830      */
6831     isSortable : function(col){
6832         if(typeof this.config[col].sortable == "undefined"){
6833             return this.defaultSortable;
6834         }
6835         return this.config[col].sortable;
6836     },
6837
6838     /**
6839      * Returns the rendering (formatting) function defined for the column.
6840      * @param {Number} col The column index.
6841      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6842      */
6843     getRenderer : function(col){
6844         if(!this.config[col].renderer){
6845             return Roo.grid.ColumnModel.defaultRenderer;
6846         }
6847         return this.config[col].renderer;
6848     },
6849
6850     /**
6851      * Sets the rendering (formatting) function for a column.
6852      * @param {Number} col The column index
6853      * @param {Function} fn The function to use to process the cell's raw data
6854      * to return HTML markup for the grid view. The render function is called with
6855      * the following parameters:<ul>
6856      * <li>Data value.</li>
6857      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6858      * <li>css A CSS style string to apply to the table cell.</li>
6859      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6860      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6861      * <li>Row index</li>
6862      * <li>Column index</li>
6863      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6864      */
6865     setRenderer : function(col, fn){
6866         this.config[col].renderer = fn;
6867     },
6868
6869     /**
6870      * Returns the width for the specified column.
6871      * @param {Number} col The column index
6872      * @return {Number}
6873      */
6874     getColumnWidth : function(col){
6875         return this.config[col].width * 1 || this.defaultWidth;
6876     },
6877
6878     /**
6879      * Sets the width for a column.
6880      * @param {Number} col The column index
6881      * @param {Number} width The new width
6882      */
6883     setColumnWidth : function(col, width, suppressEvent){
6884         this.config[col].width = width;
6885         this.totalWidth = null;
6886         if(!suppressEvent){
6887              this.fireEvent("widthchange", this, col, width);
6888         }
6889     },
6890
6891     /**
6892      * Returns the total width of all columns.
6893      * @param {Boolean} includeHidden True to include hidden column widths
6894      * @return {Number}
6895      */
6896     getTotalWidth : function(includeHidden){
6897         if(!this.totalWidth){
6898             this.totalWidth = 0;
6899             for(var i = 0, len = this.config.length; i < len; i++){
6900                 if(includeHidden || !this.isHidden(i)){
6901                     this.totalWidth += this.getColumnWidth(i);
6902                 }
6903             }
6904         }
6905         return this.totalWidth;
6906     },
6907
6908     /**
6909      * Returns the header for the specified column.
6910      * @param {Number} col The column index
6911      * @return {String}
6912      */
6913     getColumnHeader : function(col){
6914         return this.config[col].header;
6915     },
6916
6917     /**
6918      * Sets the header for a column.
6919      * @param {Number} col The column index
6920      * @param {String} header The new header
6921      */
6922     setColumnHeader : function(col, header){
6923         this.config[col].header = header;
6924         this.fireEvent("headerchange", this, col, header);
6925     },
6926
6927     /**
6928      * Returns the tooltip for the specified column.
6929      * @param {Number} col The column index
6930      * @return {String}
6931      */
6932     getColumnTooltip : function(col){
6933             return this.config[col].tooltip;
6934     },
6935     /**
6936      * Sets the tooltip for a column.
6937      * @param {Number} col The column index
6938      * @param {String} tooltip The new tooltip
6939      */
6940     setColumnTooltip : function(col, tooltip){
6941             this.config[col].tooltip = tooltip;
6942     },
6943
6944     /**
6945      * Returns the dataIndex for the specified column.
6946      * @param {Number} col The column index
6947      * @return {Number}
6948      */
6949     getDataIndex : function(col){
6950         return this.config[col].dataIndex;
6951     },
6952
6953     /**
6954      * Sets the dataIndex for a column.
6955      * @param {Number} col The column index
6956      * @param {Number} dataIndex The new dataIndex
6957      */
6958     setDataIndex : function(col, dataIndex){
6959         this.config[col].dataIndex = dataIndex;
6960     },
6961
6962     
6963     
6964     /**
6965      * Returns true if the cell is editable.
6966      * @param {Number} colIndex The column index
6967      * @param {Number} rowIndex The row index - this is nto actually used..?
6968      * @return {Boolean}
6969      */
6970     isCellEditable : function(colIndex, rowIndex){
6971         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6972     },
6973
6974     /**
6975      * Returns the editor defined for the cell/column.
6976      * return false or null to disable editing.
6977      * @param {Number} colIndex The column index
6978      * @param {Number} rowIndex The row index
6979      * @return {Object}
6980      */
6981     getCellEditor : function(colIndex, rowIndex){
6982         return this.config[colIndex].editor;
6983     },
6984
6985     /**
6986      * Sets if a column is editable.
6987      * @param {Number} col The column index
6988      * @param {Boolean} editable True if the column is editable
6989      */
6990     setEditable : function(col, editable){
6991         this.config[col].editable = editable;
6992     },
6993
6994
6995     /**
6996      * Returns true if the column is hidden.
6997      * @param {Number} colIndex The column index
6998      * @return {Boolean}
6999      */
7000     isHidden : function(colIndex){
7001         return this.config[colIndex].hidden;
7002     },
7003
7004
7005     /**
7006      * Returns true if the column width cannot be changed
7007      */
7008     isFixed : function(colIndex){
7009         return this.config[colIndex].fixed;
7010     },
7011
7012     /**
7013      * Returns true if the column can be resized
7014      * @return {Boolean}
7015      */
7016     isResizable : function(colIndex){
7017         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7018     },
7019     /**
7020      * Sets if a column is hidden.
7021      * @param {Number} colIndex The column index
7022      * @param {Boolean} hidden True if the column is hidden
7023      */
7024     setHidden : function(colIndex, hidden){
7025         this.config[colIndex].hidden = hidden;
7026         this.totalWidth = null;
7027         this.fireEvent("hiddenchange", this, colIndex, hidden);
7028     },
7029
7030     /**
7031      * Sets the editor for a column.
7032      * @param {Number} col The column index
7033      * @param {Object} editor The editor object
7034      */
7035     setEditor : function(col, editor){
7036         this.config[col].editor = editor;
7037     }
7038 });
7039
7040 Roo.grid.ColumnModel.defaultRenderer = function(value)
7041 {
7042     if(typeof value == "object") {
7043         return value;
7044     }
7045         if(typeof value == "string" && value.length < 1){
7046             return "&#160;";
7047         }
7048     
7049         return String.format("{0}", value);
7050 };
7051
7052 // Alias for backwards compatibility
7053 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7054 /*
7055  * Based on:
7056  * Ext JS Library 1.1.1
7057  * Copyright(c) 2006-2007, Ext JS, LLC.
7058  *
7059  * Originally Released Under LGPL - original licence link has changed is not relivant.
7060  *
7061  * Fork - LGPL
7062  * <script type="text/javascript">
7063  */
7064  
7065 /**
7066  * @class Roo.LoadMask
7067  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7068  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7069  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7070  * element's UpdateManager load indicator and will be destroyed after the initial load.
7071  * @constructor
7072  * Create a new LoadMask
7073  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7074  * @param {Object} config The config object
7075  */
7076 Roo.LoadMask = function(el, config){
7077     this.el = Roo.get(el);
7078     Roo.apply(this, config);
7079     if(this.store){
7080         this.store.on('beforeload', this.onBeforeLoad, this);
7081         this.store.on('load', this.onLoad, this);
7082         this.store.on('loadexception', this.onLoadException, this);
7083         this.removeMask = false;
7084     }else{
7085         var um = this.el.getUpdateManager();
7086         um.showLoadIndicator = false; // disable the default indicator
7087         um.on('beforeupdate', this.onBeforeLoad, this);
7088         um.on('update', this.onLoad, this);
7089         um.on('failure', this.onLoad, this);
7090         this.removeMask = true;
7091     }
7092 };
7093
7094 Roo.LoadMask.prototype = {
7095     /**
7096      * @cfg {Boolean} removeMask
7097      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7098      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7099      */
7100     /**
7101      * @cfg {String} msg
7102      * The text to display in a centered loading message box (defaults to 'Loading...')
7103      */
7104     msg : 'Loading...',
7105     /**
7106      * @cfg {String} msgCls
7107      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7108      */
7109     msgCls : 'x-mask-loading',
7110
7111     /**
7112      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7113      * @type Boolean
7114      */
7115     disabled: false,
7116
7117     /**
7118      * Disables the mask to prevent it from being displayed
7119      */
7120     disable : function(){
7121        this.disabled = true;
7122     },
7123
7124     /**
7125      * Enables the mask so that it can be displayed
7126      */
7127     enable : function(){
7128         this.disabled = false;
7129     },
7130     
7131     onLoadException : function()
7132     {
7133         Roo.log(arguments);
7134         
7135         if (typeof(arguments[3]) != 'undefined') {
7136             Roo.MessageBox.alert("Error loading",arguments[3]);
7137         } 
7138         /*
7139         try {
7140             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7141                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7142             }   
7143         } catch(e) {
7144             
7145         }
7146         */
7147     
7148         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7149     },
7150     // private
7151     onLoad : function()
7152     {
7153         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7154     },
7155
7156     // private
7157     onBeforeLoad : function(){
7158         if(!this.disabled){
7159             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7160         }
7161     },
7162
7163     // private
7164     destroy : function(){
7165         if(this.store){
7166             this.store.un('beforeload', this.onBeforeLoad, this);
7167             this.store.un('load', this.onLoad, this);
7168             this.store.un('loadexception', this.onLoadException, this);
7169         }else{
7170             var um = this.el.getUpdateManager();
7171             um.un('beforeupdate', this.onBeforeLoad, this);
7172             um.un('update', this.onLoad, this);
7173             um.un('failure', this.onLoad, this);
7174         }
7175     }
7176 };/*
7177  * - LGPL
7178  *
7179  * table
7180  * 
7181  */
7182
7183 /**
7184  * @class Roo.bootstrap.Table
7185  * @extends Roo.bootstrap.Component
7186  * Bootstrap Table class
7187  * @cfg {String} cls table class
7188  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7189  * @cfg {String} bgcolor Specifies the background color for a table
7190  * @cfg {Number} border Specifies whether the table cells should have borders or not
7191  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7192  * @cfg {Number} cellspacing Specifies the space between cells
7193  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7194  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7195  * @cfg {String} sortable Specifies that the table should be sortable
7196  * @cfg {String} summary Specifies a summary of the content of a table
7197  * @cfg {Number} width Specifies the width of a table
7198  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7199  * 
7200  * @cfg {boolean} striped Should the rows be alternative striped
7201  * @cfg {boolean} bordered Add borders to the table
7202  * @cfg {boolean} hover Add hover highlighting
7203  * @cfg {boolean} condensed Format condensed
7204  * @cfg {boolean} responsive Format condensed
7205  * @cfg {Boolean} loadMask (true|false) default false
7206  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7207  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7208  * @cfg {Boolean} rowSelection (true|false) default false
7209  * @cfg {Boolean} cellSelection (true|false) default false
7210  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7211  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7212  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7213  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7214  
7215  * 
7216  * @constructor
7217  * Create a new Table
7218  * @param {Object} config The config object
7219  */
7220
7221 Roo.bootstrap.Table = function(config){
7222     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7223     
7224   
7225     
7226     // BC...
7227     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7228     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7229     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7230     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7231     
7232     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7233     if (this.sm) {
7234         this.sm.grid = this;
7235         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7236         this.sm = this.selModel;
7237         this.sm.xmodule = this.xmodule || false;
7238     }
7239     
7240     if (this.cm && typeof(this.cm.config) == 'undefined') {
7241         this.colModel = new Roo.grid.ColumnModel(this.cm);
7242         this.cm = this.colModel;
7243         this.cm.xmodule = this.xmodule || false;
7244     }
7245     if (this.store) {
7246         this.store= Roo.factory(this.store, Roo.data);
7247         this.ds = this.store;
7248         this.ds.xmodule = this.xmodule || false;
7249          
7250     }
7251     if (this.footer && this.store) {
7252         this.footer.dataSource = this.ds;
7253         this.footer = Roo.factory(this.footer);
7254     }
7255     
7256     /** @private */
7257     this.addEvents({
7258         /**
7259          * @event cellclick
7260          * Fires when a cell is clicked
7261          * @param {Roo.bootstrap.Table} this
7262          * @param {Roo.Element} el
7263          * @param {Number} rowIndex
7264          * @param {Number} columnIndex
7265          * @param {Roo.EventObject} e
7266          */
7267         "cellclick" : true,
7268         /**
7269          * @event celldblclick
7270          * Fires when a cell is double clicked
7271          * @param {Roo.bootstrap.Table} this
7272          * @param {Roo.Element} el
7273          * @param {Number} rowIndex
7274          * @param {Number} columnIndex
7275          * @param {Roo.EventObject} e
7276          */
7277         "celldblclick" : true,
7278         /**
7279          * @event rowclick
7280          * Fires when a row is clicked
7281          * @param {Roo.bootstrap.Table} this
7282          * @param {Roo.Element} el
7283          * @param {Number} rowIndex
7284          * @param {Roo.EventObject} e
7285          */
7286         "rowclick" : true,
7287         /**
7288          * @event rowdblclick
7289          * Fires when a row is double clicked
7290          * @param {Roo.bootstrap.Table} this
7291          * @param {Roo.Element} el
7292          * @param {Number} rowIndex
7293          * @param {Roo.EventObject} e
7294          */
7295         "rowdblclick" : true,
7296         /**
7297          * @event mouseover
7298          * Fires when a mouseover occur
7299          * @param {Roo.bootstrap.Table} this
7300          * @param {Roo.Element} el
7301          * @param {Number} rowIndex
7302          * @param {Number} columnIndex
7303          * @param {Roo.EventObject} e
7304          */
7305         "mouseover" : true,
7306         /**
7307          * @event mouseout
7308          * Fires when a mouseout occur
7309          * @param {Roo.bootstrap.Table} this
7310          * @param {Roo.Element} el
7311          * @param {Number} rowIndex
7312          * @param {Number} columnIndex
7313          * @param {Roo.EventObject} e
7314          */
7315         "mouseout" : true,
7316         /**
7317          * @event rowclass
7318          * Fires when a row is rendered, so you can change add a style to it.
7319          * @param {Roo.bootstrap.Table} this
7320          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7321          */
7322         'rowclass' : true,
7323           /**
7324          * @event rowsrendered
7325          * Fires when all the  rows have been rendered
7326          * @param {Roo.bootstrap.Table} this
7327          */
7328         'rowsrendered' : true,
7329         /**
7330          * @event contextmenu
7331          * The raw contextmenu event for the entire grid.
7332          * @param {Roo.EventObject} e
7333          */
7334         "contextmenu" : true,
7335         /**
7336          * @event rowcontextmenu
7337          * Fires when a row is right clicked
7338          * @param {Roo.bootstrap.Table} this
7339          * @param {Number} rowIndex
7340          * @param {Roo.EventObject} e
7341          */
7342         "rowcontextmenu" : true,
7343         /**
7344          * @event cellcontextmenu
7345          * Fires when a cell is right clicked
7346          * @param {Roo.bootstrap.Table} this
7347          * @param {Number} rowIndex
7348          * @param {Number} cellIndex
7349          * @param {Roo.EventObject} e
7350          */
7351          "cellcontextmenu" : true,
7352          /**
7353          * @event headercontextmenu
7354          * Fires when a header is right clicked
7355          * @param {Roo.bootstrap.Table} this
7356          * @param {Number} columnIndex
7357          * @param {Roo.EventObject} e
7358          */
7359         "headercontextmenu" : true
7360     });
7361 };
7362
7363 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7364     
7365     cls: false,
7366     align: false,
7367     bgcolor: false,
7368     border: false,
7369     cellpadding: false,
7370     cellspacing: false,
7371     frame: false,
7372     rules: false,
7373     sortable: false,
7374     summary: false,
7375     width: false,
7376     striped : false,
7377     scrollBody : false,
7378     bordered: false,
7379     hover:  false,
7380     condensed : false,
7381     responsive : false,
7382     sm : false,
7383     cm : false,
7384     store : false,
7385     loadMask : false,
7386     footerShow : true,
7387     headerShow : true,
7388   
7389     rowSelection : false,
7390     cellSelection : false,
7391     layout : false,
7392     
7393     // Roo.Element - the tbody
7394     mainBody: false,
7395     // Roo.Element - thead element
7396     mainHead: false,
7397     
7398     container: false, // used by gridpanel...
7399     
7400     lazyLoad : false,
7401     
7402     CSS : Roo.util.CSS,
7403     
7404     auto_hide_footer : false,
7405     
7406     getAutoCreate : function()
7407     {
7408         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7409         
7410         cfg = {
7411             tag: 'table',
7412             cls : 'table',
7413             cn : []
7414         };
7415         if (this.scrollBody) {
7416             cfg.cls += ' table-body-fixed';
7417         }    
7418         if (this.striped) {
7419             cfg.cls += ' table-striped';
7420         }
7421         
7422         if (this.hover) {
7423             cfg.cls += ' table-hover';
7424         }
7425         if (this.bordered) {
7426             cfg.cls += ' table-bordered';
7427         }
7428         if (this.condensed) {
7429             cfg.cls += ' table-condensed';
7430         }
7431         if (this.responsive) {
7432             cfg.cls += ' table-responsive';
7433         }
7434         
7435         if (this.cls) {
7436             cfg.cls+=  ' ' +this.cls;
7437         }
7438         
7439         // this lot should be simplifed...
7440         var _t = this;
7441         var cp = [
7442             'align',
7443             'bgcolor',
7444             'border',
7445             'cellpadding',
7446             'cellspacing',
7447             'frame',
7448             'rules',
7449             'sortable',
7450             'summary',
7451             'width'
7452         ].forEach(function(k) {
7453             if (_t[k]) {
7454                 cfg[k] = _t[k];
7455             }
7456         });
7457         
7458         
7459         if (this.layout) {
7460             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7461         }
7462         
7463         if(this.store || this.cm){
7464             if(this.headerShow){
7465                 cfg.cn.push(this.renderHeader());
7466             }
7467             
7468             cfg.cn.push(this.renderBody());
7469             
7470             if(this.footerShow){
7471                 cfg.cn.push(this.renderFooter());
7472             }
7473             // where does this come from?
7474             //cfg.cls+=  ' TableGrid';
7475         }
7476         
7477         return { cn : [ cfg ] };
7478     },
7479     
7480     initEvents : function()
7481     {   
7482         if(!this.store || !this.cm){
7483             return;
7484         }
7485         if (this.selModel) {
7486             this.selModel.initEvents();
7487         }
7488         
7489         
7490         //Roo.log('initEvents with ds!!!!');
7491         
7492         this.mainBody = this.el.select('tbody', true).first();
7493         this.mainHead = this.el.select('thead', true).first();
7494         this.mainFoot = this.el.select('tfoot', true).first();
7495         
7496         
7497         
7498         var _this = this;
7499         
7500         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7501             e.on('click', _this.sort, _this);
7502         });
7503         
7504         this.mainBody.on("click", this.onClick, this);
7505         this.mainBody.on("dblclick", this.onDblClick, this);
7506         
7507         // why is this done????? = it breaks dialogs??
7508         //this.parent().el.setStyle('position', 'relative');
7509         
7510         
7511         if (this.footer) {
7512             this.footer.parentId = this.id;
7513             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7514             
7515             if(this.lazyLoad){
7516                 this.el.select('tfoot tr td').first().addClass('hide');
7517             }
7518         } 
7519         
7520         if(this.loadMask) {
7521             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7522         }
7523         
7524         this.store.on('load', this.onLoad, this);
7525         this.store.on('beforeload', this.onBeforeLoad, this);
7526         this.store.on('update', this.onUpdate, this);
7527         this.store.on('add', this.onAdd, this);
7528         this.store.on("clear", this.clear, this);
7529         
7530         this.el.on("contextmenu", this.onContextMenu, this);
7531         
7532         this.mainBody.on('scroll', this.onBodyScroll, this);
7533         
7534         this.cm.on("headerchange", this.onHeaderChange, this);
7535         
7536         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7537         
7538     },
7539     
7540     onContextMenu : function(e, t)
7541     {
7542         this.processEvent("contextmenu", e);
7543     },
7544     
7545     processEvent : function(name, e)
7546     {
7547         if (name != 'touchstart' ) {
7548             this.fireEvent(name, e);    
7549         }
7550         
7551         var t = e.getTarget();
7552         
7553         var cell = Roo.get(t);
7554         
7555         if(!cell){
7556             return;
7557         }
7558         
7559         if(cell.findParent('tfoot', false, true)){
7560             return;
7561         }
7562         
7563         if(cell.findParent('thead', false, true)){
7564             
7565             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7566                 cell = Roo.get(t).findParent('th', false, true);
7567                 if (!cell) {
7568                     Roo.log("failed to find th in thead?");
7569                     Roo.log(e.getTarget());
7570                     return;
7571                 }
7572             }
7573             
7574             var cellIndex = cell.dom.cellIndex;
7575             
7576             var ename = name == 'touchstart' ? 'click' : name;
7577             this.fireEvent("header" + ename, this, cellIndex, e);
7578             
7579             return;
7580         }
7581         
7582         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7583             cell = Roo.get(t).findParent('td', false, true);
7584             if (!cell) {
7585                 Roo.log("failed to find th in tbody?");
7586                 Roo.log(e.getTarget());
7587                 return;
7588             }
7589         }
7590         
7591         var row = cell.findParent('tr', false, true);
7592         var cellIndex = cell.dom.cellIndex;
7593         var rowIndex = row.dom.rowIndex - 1;
7594         
7595         if(row !== false){
7596             
7597             this.fireEvent("row" + name, this, rowIndex, e);
7598             
7599             if(cell !== false){
7600             
7601                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7602             }
7603         }
7604         
7605     },
7606     
7607     onMouseover : function(e, el)
7608     {
7609         var cell = Roo.get(el);
7610         
7611         if(!cell){
7612             return;
7613         }
7614         
7615         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7616             cell = cell.findParent('td', false, true);
7617         }
7618         
7619         var row = cell.findParent('tr', false, true);
7620         var cellIndex = cell.dom.cellIndex;
7621         var rowIndex = row.dom.rowIndex - 1; // start from 0
7622         
7623         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7624         
7625     },
7626     
7627     onMouseout : function(e, el)
7628     {
7629         var cell = Roo.get(el);
7630         
7631         if(!cell){
7632             return;
7633         }
7634         
7635         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7636             cell = cell.findParent('td', false, true);
7637         }
7638         
7639         var row = cell.findParent('tr', false, true);
7640         var cellIndex = cell.dom.cellIndex;
7641         var rowIndex = row.dom.rowIndex - 1; // start from 0
7642         
7643         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7644         
7645     },
7646     
7647     onClick : function(e, el)
7648     {
7649         var cell = Roo.get(el);
7650         
7651         if(!cell || (!this.cellSelection && !this.rowSelection)){
7652             return;
7653         }
7654         
7655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7656             cell = cell.findParent('td', false, true);
7657         }
7658         
7659         if(!cell || typeof(cell) == 'undefined'){
7660             return;
7661         }
7662         
7663         var row = cell.findParent('tr', false, true);
7664         
7665         if(!row || typeof(row) == 'undefined'){
7666             return;
7667         }
7668         
7669         var cellIndex = cell.dom.cellIndex;
7670         var rowIndex = this.getRowIndex(row);
7671         
7672         // why??? - should these not be based on SelectionModel?
7673         if(this.cellSelection){
7674             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7675         }
7676         
7677         if(this.rowSelection){
7678             this.fireEvent('rowclick', this, row, rowIndex, e);
7679         }
7680         
7681         
7682     },
7683         
7684     onDblClick : function(e,el)
7685     {
7686         var cell = Roo.get(el);
7687         
7688         if(!cell || (!this.cellSelection && !this.rowSelection)){
7689             return;
7690         }
7691         
7692         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7693             cell = cell.findParent('td', false, true);
7694         }
7695         
7696         if(!cell || typeof(cell) == 'undefined'){
7697             return;
7698         }
7699         
7700         var row = cell.findParent('tr', false, true);
7701         
7702         if(!row || typeof(row) == 'undefined'){
7703             return;
7704         }
7705         
7706         var cellIndex = cell.dom.cellIndex;
7707         var rowIndex = this.getRowIndex(row);
7708         
7709         if(this.cellSelection){
7710             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7711         }
7712         
7713         if(this.rowSelection){
7714             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7715         }
7716     },
7717     
7718     sort : function(e,el)
7719     {
7720         var col = Roo.get(el);
7721         
7722         if(!col.hasClass('sortable')){
7723             return;
7724         }
7725         
7726         var sort = col.attr('sort');
7727         var dir = 'ASC';
7728         
7729         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7730             dir = 'DESC';
7731         }
7732         
7733         this.store.sortInfo = {field : sort, direction : dir};
7734         
7735         if (this.footer) {
7736             Roo.log("calling footer first");
7737             this.footer.onClick('first');
7738         } else {
7739         
7740             this.store.load({ params : { start : 0 } });
7741         }
7742     },
7743     
7744     renderHeader : function()
7745     {
7746         var header = {
7747             tag: 'thead',
7748             cn : []
7749         };
7750         
7751         var cm = this.cm;
7752         this.totalWidth = 0;
7753         
7754         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7755             
7756             var config = cm.config[i];
7757             
7758             var c = {
7759                 tag: 'th',
7760                 cls : 'x-hcol-' + i,
7761                 style : '',
7762                 html: cm.getColumnHeader(i)
7763             };
7764             
7765             var hh = '';
7766             
7767             if(typeof(config.sortable) != 'undefined' && config.sortable){
7768                 c.cls = 'sortable';
7769                 c.html = '<i class="glyphicon"></i>' + c.html;
7770             }
7771             
7772             // could use BS4 hidden-..-down 
7773             
7774             if(typeof(config.lgHeader) != 'undefined'){
7775                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7776             }
7777             
7778             if(typeof(config.mdHeader) != 'undefined'){
7779                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7780             }
7781             
7782             if(typeof(config.smHeader) != 'undefined'){
7783                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7784             }
7785             
7786             if(typeof(config.xsHeader) != 'undefined'){
7787                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7788             }
7789             
7790             if(hh.length){
7791                 c.html = hh;
7792             }
7793             
7794             if(typeof(config.tooltip) != 'undefined'){
7795                 c.tooltip = config.tooltip;
7796             }
7797             
7798             if(typeof(config.colspan) != 'undefined'){
7799                 c.colspan = config.colspan;
7800             }
7801             
7802             if(typeof(config.hidden) != 'undefined' && config.hidden){
7803                 c.style += ' display:none;';
7804             }
7805             
7806             if(typeof(config.dataIndex) != 'undefined'){
7807                 c.sort = config.dataIndex;
7808             }
7809             
7810            
7811             
7812             if(typeof(config.align) != 'undefined' && config.align.length){
7813                 c.style += ' text-align:' + config.align + ';';
7814             }
7815             
7816             if(typeof(config.width) != 'undefined'){
7817                 c.style += ' width:' + config.width + 'px;';
7818                 this.totalWidth += config.width;
7819             } else {
7820                 this.totalWidth += 100; // assume minimum of 100 per column?
7821             }
7822             
7823             if(typeof(config.cls) != 'undefined'){
7824                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7825             }
7826             
7827             ['xs','sm','md','lg'].map(function(size){
7828                 
7829                 if(typeof(config[size]) == 'undefined'){
7830                     return;
7831                 }
7832                  
7833                 if (!config[size]) { // 0 = hidden
7834                     // BS 4 '0' is treated as hide that column and below.
7835                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7836                     return;
7837                 }
7838                 
7839                 c.cls += ' col-' + size + '-' + config[size] + (
7840                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7841                 );
7842                 
7843                 
7844             });
7845             
7846             header.cn.push(c)
7847         }
7848         
7849         return header;
7850     },
7851     
7852     renderBody : function()
7853     {
7854         var body = {
7855             tag: 'tbody',
7856             cn : [
7857                 {
7858                     tag: 'tr',
7859                     cn : [
7860                         {
7861                             tag : 'td',
7862                             colspan :  this.cm.getColumnCount()
7863                         }
7864                     ]
7865                 }
7866             ]
7867         };
7868         
7869         return body;
7870     },
7871     
7872     renderFooter : function()
7873     {
7874         var footer = {
7875             tag: 'tfoot',
7876             cn : [
7877                 {
7878                     tag: 'tr',
7879                     cn : [
7880                         {
7881                             tag : 'td',
7882                             colspan :  this.cm.getColumnCount()
7883                         }
7884                     ]
7885                 }
7886             ]
7887         };
7888         
7889         return footer;
7890     },
7891     
7892     
7893     
7894     onLoad : function()
7895     {
7896 //        Roo.log('ds onload');
7897         this.clear();
7898         
7899         var _this = this;
7900         var cm = this.cm;
7901         var ds = this.store;
7902         
7903         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7904             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7905             if (_this.store.sortInfo) {
7906                     
7907                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7908                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7909                 }
7910                 
7911                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7912                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7913                 }
7914             }
7915         });
7916         
7917         var tbody =  this.mainBody;
7918               
7919         if(ds.getCount() > 0){
7920             ds.data.each(function(d,rowIndex){
7921                 var row =  this.renderRow(cm, ds, rowIndex);
7922                 
7923                 tbody.createChild(row);
7924                 
7925                 var _this = this;
7926                 
7927                 if(row.cellObjects.length){
7928                     Roo.each(row.cellObjects, function(r){
7929                         _this.renderCellObject(r);
7930                     })
7931                 }
7932                 
7933             }, this);
7934         }
7935         
7936         var tfoot = this.el.select('tfoot', true).first();
7937         
7938         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7939             
7940             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7941             
7942             var total = this.ds.getTotalCount();
7943             
7944             if(this.footer.pageSize < total){
7945                 this.mainFoot.show();
7946             }
7947         }
7948         
7949         Roo.each(this.el.select('tbody td', true).elements, function(e){
7950             e.on('mouseover', _this.onMouseover, _this);
7951         });
7952         
7953         Roo.each(this.el.select('tbody td', true).elements, function(e){
7954             e.on('mouseout', _this.onMouseout, _this);
7955         });
7956         this.fireEvent('rowsrendered', this);
7957         
7958         this.autoSize();
7959     },
7960     
7961     
7962     onUpdate : function(ds,record)
7963     {
7964         this.refreshRow(record);
7965         this.autoSize();
7966     },
7967     
7968     onRemove : function(ds, record, index, isUpdate){
7969         if(isUpdate !== true){
7970             this.fireEvent("beforerowremoved", this, index, record);
7971         }
7972         var bt = this.mainBody.dom;
7973         
7974         var rows = this.el.select('tbody > tr', true).elements;
7975         
7976         if(typeof(rows[index]) != 'undefined'){
7977             bt.removeChild(rows[index].dom);
7978         }
7979         
7980 //        if(bt.rows[index]){
7981 //            bt.removeChild(bt.rows[index]);
7982 //        }
7983         
7984         if(isUpdate !== true){
7985             //this.stripeRows(index);
7986             //this.syncRowHeights(index, index);
7987             //this.layout();
7988             this.fireEvent("rowremoved", this, index, record);
7989         }
7990     },
7991     
7992     onAdd : function(ds, records, rowIndex)
7993     {
7994         //Roo.log('on Add called');
7995         // - note this does not handle multiple adding very well..
7996         var bt = this.mainBody.dom;
7997         for (var i =0 ; i < records.length;i++) {
7998             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7999             //Roo.log(records[i]);
8000             //Roo.log(this.store.getAt(rowIndex+i));
8001             this.insertRow(this.store, rowIndex + i, false);
8002             return;
8003         }
8004         
8005     },
8006     
8007     
8008     refreshRow : function(record){
8009         var ds = this.store, index;
8010         if(typeof record == 'number'){
8011             index = record;
8012             record = ds.getAt(index);
8013         }else{
8014             index = ds.indexOf(record);
8015         }
8016         this.insertRow(ds, index, true);
8017         this.autoSize();
8018         this.onRemove(ds, record, index+1, true);
8019         this.autoSize();
8020         //this.syncRowHeights(index, index);
8021         //this.layout();
8022         this.fireEvent("rowupdated", this, index, record);
8023     },
8024     
8025     insertRow : function(dm, rowIndex, isUpdate){
8026         
8027         if(!isUpdate){
8028             this.fireEvent("beforerowsinserted", this, rowIndex);
8029         }
8030             //var s = this.getScrollState();
8031         var row = this.renderRow(this.cm, this.store, rowIndex);
8032         // insert before rowIndex..
8033         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8034         
8035         var _this = this;
8036                 
8037         if(row.cellObjects.length){
8038             Roo.each(row.cellObjects, function(r){
8039                 _this.renderCellObject(r);
8040             })
8041         }
8042             
8043         if(!isUpdate){
8044             this.fireEvent("rowsinserted", this, rowIndex);
8045             //this.syncRowHeights(firstRow, lastRow);
8046             //this.stripeRows(firstRow);
8047             //this.layout();
8048         }
8049         
8050     },
8051     
8052     
8053     getRowDom : function(rowIndex)
8054     {
8055         var rows = this.el.select('tbody > tr', true).elements;
8056         
8057         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8058         
8059     },
8060     // returns the object tree for a tr..
8061   
8062     
8063     renderRow : function(cm, ds, rowIndex) 
8064     {
8065         var d = ds.getAt(rowIndex);
8066         
8067         var row = {
8068             tag : 'tr',
8069             cls : 'x-row-' + rowIndex,
8070             cn : []
8071         };
8072             
8073         var cellObjects = [];
8074         
8075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076             var config = cm.config[i];
8077             
8078             var renderer = cm.getRenderer(i);
8079             var value = '';
8080             var id = false;
8081             
8082             if(typeof(renderer) !== 'undefined'){
8083                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8084             }
8085             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8086             // and are rendered into the cells after the row is rendered - using the id for the element.
8087             
8088             if(typeof(value) === 'object'){
8089                 id = Roo.id();
8090                 cellObjects.push({
8091                     container : id,
8092                     cfg : value 
8093                 })
8094             }
8095             
8096             var rowcfg = {
8097                 record: d,
8098                 rowIndex : rowIndex,
8099                 colIndex : i,
8100                 rowClass : ''
8101             };
8102
8103             this.fireEvent('rowclass', this, rowcfg);
8104             
8105             var td = {
8106                 tag: 'td',
8107                 cls : rowcfg.rowClass + ' x-col-' + i,
8108                 style: '',
8109                 html: (typeof(value) === 'object') ? '' : value
8110             };
8111             
8112             if (id) {
8113                 td.id = id;
8114             }
8115             
8116             if(typeof(config.colspan) != 'undefined'){
8117                 td.colspan = config.colspan;
8118             }
8119             
8120             if(typeof(config.hidden) != 'undefined' && config.hidden){
8121                 td.style += ' display:none;';
8122             }
8123             
8124             if(typeof(config.align) != 'undefined' && config.align.length){
8125                 td.style += ' text-align:' + config.align + ';';
8126             }
8127             if(typeof(config.valign) != 'undefined' && config.valign.length){
8128                 td.style += ' vertical-align:' + config.valign + ';';
8129             }
8130             
8131             if(typeof(config.width) != 'undefined'){
8132                 td.style += ' width:' +  config.width + 'px;';
8133             }
8134             
8135             if(typeof(config.cursor) != 'undefined'){
8136                 td.style += ' cursor:' +  config.cursor + ';';
8137             }
8138             
8139             if(typeof(config.cls) != 'undefined'){
8140                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8141             }
8142             
8143             ['xs','sm','md','lg'].map(function(size){
8144                 
8145                 if(typeof(config[size]) == 'undefined'){
8146                     return;
8147                 }
8148                 
8149                 
8150                   
8151                 if (!config[size]) { // 0 = hidden
8152                     // BS 4 '0' is treated as hide that column and below.
8153                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8154                     return;
8155                 }
8156                 
8157                 td.cls += ' col-' + size + '-' + config[size] + (
8158                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8159                 );
8160                  
8161
8162             });
8163             
8164             row.cn.push(td);
8165            
8166         }
8167         
8168         row.cellObjects = cellObjects;
8169         
8170         return row;
8171           
8172     },
8173     
8174     
8175     
8176     onBeforeLoad : function()
8177     {
8178         
8179     },
8180      /**
8181      * Remove all rows
8182      */
8183     clear : function()
8184     {
8185         this.el.select('tbody', true).first().dom.innerHTML = '';
8186     },
8187     /**
8188      * Show or hide a row.
8189      * @param {Number} rowIndex to show or hide
8190      * @param {Boolean} state hide
8191      */
8192     setRowVisibility : function(rowIndex, state)
8193     {
8194         var bt = this.mainBody.dom;
8195         
8196         var rows = this.el.select('tbody > tr', true).elements;
8197         
8198         if(typeof(rows[rowIndex]) == 'undefined'){
8199             return;
8200         }
8201         rows[rowIndex].dom.style.display = state ? '' : 'none';
8202     },
8203     
8204     
8205     getSelectionModel : function(){
8206         if(!this.selModel){
8207             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8208         }
8209         return this.selModel;
8210     },
8211     /*
8212      * Render the Roo.bootstrap object from renderder
8213      */
8214     renderCellObject : function(r)
8215     {
8216         var _this = this;
8217         
8218         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8219         
8220         var t = r.cfg.render(r.container);
8221         
8222         if(r.cfg.cn){
8223             Roo.each(r.cfg.cn, function(c){
8224                 var child = {
8225                     container: t.getChildContainer(),
8226                     cfg: c
8227                 };
8228                 _this.renderCellObject(child);
8229             })
8230         }
8231     },
8232     
8233     getRowIndex : function(row)
8234     {
8235         var rowIndex = -1;
8236         
8237         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8238             if(el != row){
8239                 return;
8240             }
8241             
8242             rowIndex = index;
8243         });
8244         
8245         return rowIndex;
8246     },
8247      /**
8248      * Returns the grid's underlying element = used by panel.Grid
8249      * @return {Element} The element
8250      */
8251     getGridEl : function(){
8252         return this.el;
8253     },
8254      /**
8255      * Forces a resize - used by panel.Grid
8256      * @return {Element} The element
8257      */
8258     autoSize : function()
8259     {
8260         //var ctr = Roo.get(this.container.dom.parentElement);
8261         var ctr = Roo.get(this.el.dom);
8262         
8263         var thd = this.getGridEl().select('thead',true).first();
8264         var tbd = this.getGridEl().select('tbody', true).first();
8265         var tfd = this.getGridEl().select('tfoot', true).first();
8266         
8267         var cw = ctr.getWidth();
8268         
8269         if (tbd) {
8270             
8271             tbd.setWidth(ctr.getWidth());
8272             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8273             // this needs fixing for various usage - currently only hydra job advers I think..
8274             //tdb.setHeight(
8275             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8276             //); 
8277             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8278             cw -= barsize;
8279         }
8280         cw = Math.max(cw, this.totalWidth);
8281         this.getGridEl().select('tr',true).setWidth(cw);
8282         // resize 'expandable coloumn?
8283         
8284         return; // we doe not have a view in this design..
8285         
8286     },
8287     onBodyScroll: function()
8288     {
8289         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8290         if(this.mainHead){
8291             this.mainHead.setStyle({
8292                 'position' : 'relative',
8293                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8294             });
8295         }
8296         
8297         if(this.lazyLoad){
8298             
8299             var scrollHeight = this.mainBody.dom.scrollHeight;
8300             
8301             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8302             
8303             var height = this.mainBody.getHeight();
8304             
8305             if(scrollHeight - height == scrollTop) {
8306                 
8307                 var total = this.ds.getTotalCount();
8308                 
8309                 if(this.footer.cursor + this.footer.pageSize < total){
8310                     
8311                     this.footer.ds.load({
8312                         params : {
8313                             start : this.footer.cursor + this.footer.pageSize,
8314                             limit : this.footer.pageSize
8315                         },
8316                         add : true
8317                     });
8318                 }
8319             }
8320             
8321         }
8322     },
8323     
8324     onHeaderChange : function()
8325     {
8326         var header = this.renderHeader();
8327         var table = this.el.select('table', true).first();
8328         
8329         this.mainHead.remove();
8330         this.mainHead = table.createChild(header, this.mainBody, false);
8331     },
8332     
8333     onHiddenChange : function(colModel, colIndex, hidden)
8334     {
8335         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8336         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8337         
8338         this.CSS.updateRule(thSelector, "display", "");
8339         this.CSS.updateRule(tdSelector, "display", "");
8340         
8341         if(hidden){
8342             this.CSS.updateRule(thSelector, "display", "none");
8343             this.CSS.updateRule(tdSelector, "display", "none");
8344         }
8345         
8346         this.onHeaderChange();
8347         this.onLoad();
8348     },
8349     
8350     setColumnWidth: function(col_index, width)
8351     {
8352         // width = "md-2 xs-2..."
8353         if(!this.colModel.config[col_index]) {
8354             return;
8355         }
8356         
8357         var w = width.split(" ");
8358         
8359         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8360         
8361         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8362         
8363         
8364         for(var j = 0; j < w.length; j++) {
8365             
8366             if(!w[j]) {
8367                 continue;
8368             }
8369             
8370             var size_cls = w[j].split("-");
8371             
8372             if(!Number.isInteger(size_cls[1] * 1)) {
8373                 continue;
8374             }
8375             
8376             if(!this.colModel.config[col_index][size_cls[0]]) {
8377                 continue;
8378             }
8379             
8380             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8381                 continue;
8382             }
8383             
8384             h_row[0].classList.replace(
8385                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8386                 "col-"+size_cls[0]+"-"+size_cls[1]
8387             );
8388             
8389             for(var i = 0; i < rows.length; i++) {
8390                 
8391                 var size_cls = w[j].split("-");
8392                 
8393                 if(!Number.isInteger(size_cls[1] * 1)) {
8394                     continue;
8395                 }
8396                 
8397                 if(!this.colModel.config[col_index][size_cls[0]]) {
8398                     continue;
8399                 }
8400                 
8401                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8402                     continue;
8403                 }
8404                 
8405                 rows[i].classList.replace(
8406                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8407                     "col-"+size_cls[0]+"-"+size_cls[1]
8408                 );
8409             }
8410             
8411             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8412         }
8413     }
8414 });
8415
8416  
8417
8418  /*
8419  * - LGPL
8420  *
8421  * table cell
8422  * 
8423  */
8424
8425 /**
8426  * @class Roo.bootstrap.TableCell
8427  * @extends Roo.bootstrap.Component
8428  * Bootstrap TableCell class
8429  * @cfg {String} html cell contain text
8430  * @cfg {String} cls cell class
8431  * @cfg {String} tag cell tag (td|th) default td
8432  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8433  * @cfg {String} align Aligns the content in a cell
8434  * @cfg {String} axis Categorizes cells
8435  * @cfg {String} bgcolor Specifies the background color of a cell
8436  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8437  * @cfg {Number} colspan Specifies the number of columns a cell should span
8438  * @cfg {String} headers Specifies one or more header cells a cell is related to
8439  * @cfg {Number} height Sets the height of a cell
8440  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8441  * @cfg {Number} rowspan Sets the number of rows a cell should span
8442  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8443  * @cfg {String} valign Vertical aligns the content in a cell
8444  * @cfg {Number} width Specifies the width of a cell
8445  * 
8446  * @constructor
8447  * Create a new TableCell
8448  * @param {Object} config The config object
8449  */
8450
8451 Roo.bootstrap.TableCell = function(config){
8452     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8453 };
8454
8455 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8456     
8457     html: false,
8458     cls: false,
8459     tag: false,
8460     abbr: false,
8461     align: false,
8462     axis: false,
8463     bgcolor: false,
8464     charoff: false,
8465     colspan: false,
8466     headers: false,
8467     height: false,
8468     nowrap: false,
8469     rowspan: false,
8470     scope: false,
8471     valign: false,
8472     width: false,
8473     
8474     
8475     getAutoCreate : function(){
8476         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8477         
8478         cfg = {
8479             tag: 'td'
8480         };
8481         
8482         if(this.tag){
8483             cfg.tag = this.tag;
8484         }
8485         
8486         if (this.html) {
8487             cfg.html=this.html
8488         }
8489         if (this.cls) {
8490             cfg.cls=this.cls
8491         }
8492         if (this.abbr) {
8493             cfg.abbr=this.abbr
8494         }
8495         if (this.align) {
8496             cfg.align=this.align
8497         }
8498         if (this.axis) {
8499             cfg.axis=this.axis
8500         }
8501         if (this.bgcolor) {
8502             cfg.bgcolor=this.bgcolor
8503         }
8504         if (this.charoff) {
8505             cfg.charoff=this.charoff
8506         }
8507         if (this.colspan) {
8508             cfg.colspan=this.colspan
8509         }
8510         if (this.headers) {
8511             cfg.headers=this.headers
8512         }
8513         if (this.height) {
8514             cfg.height=this.height
8515         }
8516         if (this.nowrap) {
8517             cfg.nowrap=this.nowrap
8518         }
8519         if (this.rowspan) {
8520             cfg.rowspan=this.rowspan
8521         }
8522         if (this.scope) {
8523             cfg.scope=this.scope
8524         }
8525         if (this.valign) {
8526             cfg.valign=this.valign
8527         }
8528         if (this.width) {
8529             cfg.width=this.width
8530         }
8531         
8532         
8533         return cfg;
8534     }
8535    
8536 });
8537
8538  
8539
8540  /*
8541  * - LGPL
8542  *
8543  * table row
8544  * 
8545  */
8546
8547 /**
8548  * @class Roo.bootstrap.TableRow
8549  * @extends Roo.bootstrap.Component
8550  * Bootstrap TableRow class
8551  * @cfg {String} cls row class
8552  * @cfg {String} align Aligns the content in a table row
8553  * @cfg {String} bgcolor Specifies a background color for a table row
8554  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8555  * @cfg {String} valign Vertical aligns the content in a table row
8556  * 
8557  * @constructor
8558  * Create a new TableRow
8559  * @param {Object} config The config object
8560  */
8561
8562 Roo.bootstrap.TableRow = function(config){
8563     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8564 };
8565
8566 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8567     
8568     cls: false,
8569     align: false,
8570     bgcolor: false,
8571     charoff: false,
8572     valign: false,
8573     
8574     getAutoCreate : function(){
8575         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8576         
8577         cfg = {
8578             tag: 'tr'
8579         };
8580             
8581         if(this.cls){
8582             cfg.cls = this.cls;
8583         }
8584         if(this.align){
8585             cfg.align = this.align;
8586         }
8587         if(this.bgcolor){
8588             cfg.bgcolor = this.bgcolor;
8589         }
8590         if(this.charoff){
8591             cfg.charoff = this.charoff;
8592         }
8593         if(this.valign){
8594             cfg.valign = this.valign;
8595         }
8596         
8597         return cfg;
8598     }
8599    
8600 });
8601
8602  
8603
8604  /*
8605  * - LGPL
8606  *
8607  * table body
8608  * 
8609  */
8610
8611 /**
8612  * @class Roo.bootstrap.TableBody
8613  * @extends Roo.bootstrap.Component
8614  * Bootstrap TableBody class
8615  * @cfg {String} cls element class
8616  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8617  * @cfg {String} align Aligns the content inside the element
8618  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8619  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8620  * 
8621  * @constructor
8622  * Create a new TableBody
8623  * @param {Object} config The config object
8624  */
8625
8626 Roo.bootstrap.TableBody = function(config){
8627     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8628 };
8629
8630 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8631     
8632     cls: false,
8633     tag: false,
8634     align: false,
8635     charoff: false,
8636     valign: false,
8637     
8638     getAutoCreate : function(){
8639         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8640         
8641         cfg = {
8642             tag: 'tbody'
8643         };
8644             
8645         if (this.cls) {
8646             cfg.cls=this.cls
8647         }
8648         if(this.tag){
8649             cfg.tag = this.tag;
8650         }
8651         
8652         if(this.align){
8653             cfg.align = this.align;
8654         }
8655         if(this.charoff){
8656             cfg.charoff = this.charoff;
8657         }
8658         if(this.valign){
8659             cfg.valign = this.valign;
8660         }
8661         
8662         return cfg;
8663     }
8664     
8665     
8666 //    initEvents : function()
8667 //    {
8668 //        
8669 //        if(!this.store){
8670 //            return;
8671 //        }
8672 //        
8673 //        this.store = Roo.factory(this.store, Roo.data);
8674 //        this.store.on('load', this.onLoad, this);
8675 //        
8676 //        this.store.load();
8677 //        
8678 //    },
8679 //    
8680 //    onLoad: function () 
8681 //    {   
8682 //        this.fireEvent('load', this);
8683 //    }
8684 //    
8685 //   
8686 });
8687
8688  
8689
8690  /*
8691  * Based on:
8692  * Ext JS Library 1.1.1
8693  * Copyright(c) 2006-2007, Ext JS, LLC.
8694  *
8695  * Originally Released Under LGPL - original licence link has changed is not relivant.
8696  *
8697  * Fork - LGPL
8698  * <script type="text/javascript">
8699  */
8700
8701 // as we use this in bootstrap.
8702 Roo.namespace('Roo.form');
8703  /**
8704  * @class Roo.form.Action
8705  * Internal Class used to handle form actions
8706  * @constructor
8707  * @param {Roo.form.BasicForm} el The form element or its id
8708  * @param {Object} config Configuration options
8709  */
8710
8711  
8712  
8713 // define the action interface
8714 Roo.form.Action = function(form, options){
8715     this.form = form;
8716     this.options = options || {};
8717 };
8718 /**
8719  * Client Validation Failed
8720  * @const 
8721  */
8722 Roo.form.Action.CLIENT_INVALID = 'client';
8723 /**
8724  * Server Validation Failed
8725  * @const 
8726  */
8727 Roo.form.Action.SERVER_INVALID = 'server';
8728  /**
8729  * Connect to Server Failed
8730  * @const 
8731  */
8732 Roo.form.Action.CONNECT_FAILURE = 'connect';
8733 /**
8734  * Reading Data from Server Failed
8735  * @const 
8736  */
8737 Roo.form.Action.LOAD_FAILURE = 'load';
8738
8739 Roo.form.Action.prototype = {
8740     type : 'default',
8741     failureType : undefined,
8742     response : undefined,
8743     result : undefined,
8744
8745     // interface method
8746     run : function(options){
8747
8748     },
8749
8750     // interface method
8751     success : function(response){
8752
8753     },
8754
8755     // interface method
8756     handleResponse : function(response){
8757
8758     },
8759
8760     // default connection failure
8761     failure : function(response){
8762         
8763         this.response = response;
8764         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8765         this.form.afterAction(this, false);
8766     },
8767
8768     processResponse : function(response){
8769         this.response = response;
8770         if(!response.responseText){
8771             return true;
8772         }
8773         this.result = this.handleResponse(response);
8774         return this.result;
8775     },
8776
8777     // utility functions used internally
8778     getUrl : function(appendParams){
8779         var url = this.options.url || this.form.url || this.form.el.dom.action;
8780         if(appendParams){
8781             var p = this.getParams();
8782             if(p){
8783                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8784             }
8785         }
8786         return url;
8787     },
8788
8789     getMethod : function(){
8790         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8791     },
8792
8793     getParams : function(){
8794         var bp = this.form.baseParams;
8795         var p = this.options.params;
8796         if(p){
8797             if(typeof p == "object"){
8798                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8799             }else if(typeof p == 'string' && bp){
8800                 p += '&' + Roo.urlEncode(bp);
8801             }
8802         }else if(bp){
8803             p = Roo.urlEncode(bp);
8804         }
8805         return p;
8806     },
8807
8808     createCallback : function(){
8809         return {
8810             success: this.success,
8811             failure: this.failure,
8812             scope: this,
8813             timeout: (this.form.timeout*1000),
8814             upload: this.form.fileUpload ? this.success : undefined
8815         };
8816     }
8817 };
8818
8819 Roo.form.Action.Submit = function(form, options){
8820     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8821 };
8822
8823 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8824     type : 'submit',
8825
8826     haveProgress : false,
8827     uploadComplete : false,
8828     
8829     // uploadProgress indicator.
8830     uploadProgress : function()
8831     {
8832         if (!this.form.progressUrl) {
8833             return;
8834         }
8835         
8836         if (!this.haveProgress) {
8837             Roo.MessageBox.progress("Uploading", "Uploading");
8838         }
8839         if (this.uploadComplete) {
8840            Roo.MessageBox.hide();
8841            return;
8842         }
8843         
8844         this.haveProgress = true;
8845    
8846         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8847         
8848         var c = new Roo.data.Connection();
8849         c.request({
8850             url : this.form.progressUrl,
8851             params: {
8852                 id : uid
8853             },
8854             method: 'GET',
8855             success : function(req){
8856                //console.log(data);
8857                 var rdata = false;
8858                 var edata;
8859                 try  {
8860                    rdata = Roo.decode(req.responseText)
8861                 } catch (e) {
8862                     Roo.log("Invalid data from server..");
8863                     Roo.log(edata);
8864                     return;
8865                 }
8866                 if (!rdata || !rdata.success) {
8867                     Roo.log(rdata);
8868                     Roo.MessageBox.alert(Roo.encode(rdata));
8869                     return;
8870                 }
8871                 var data = rdata.data;
8872                 
8873                 if (this.uploadComplete) {
8874                    Roo.MessageBox.hide();
8875                    return;
8876                 }
8877                    
8878                 if (data){
8879                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8880                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8881                     );
8882                 }
8883                 this.uploadProgress.defer(2000,this);
8884             },
8885        
8886             failure: function(data) {
8887                 Roo.log('progress url failed ');
8888                 Roo.log(data);
8889             },
8890             scope : this
8891         });
8892            
8893     },
8894     
8895     
8896     run : function()
8897     {
8898         // run get Values on the form, so it syncs any secondary forms.
8899         this.form.getValues();
8900         
8901         var o = this.options;
8902         var method = this.getMethod();
8903         var isPost = method == 'POST';
8904         if(o.clientValidation === false || this.form.isValid()){
8905             
8906             if (this.form.progressUrl) {
8907                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8908                     (new Date() * 1) + '' + Math.random());
8909                     
8910             } 
8911             
8912             
8913             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8914                 form:this.form.el.dom,
8915                 url:this.getUrl(!isPost),
8916                 method: method,
8917                 params:isPost ? this.getParams() : null,
8918                 isUpload: this.form.fileUpload,
8919                 formData : this.form.formData
8920             }));
8921             
8922             this.uploadProgress();
8923
8924         }else if (o.clientValidation !== false){ // client validation failed
8925             this.failureType = Roo.form.Action.CLIENT_INVALID;
8926             this.form.afterAction(this, false);
8927         }
8928     },
8929
8930     success : function(response)
8931     {
8932         this.uploadComplete= true;
8933         if (this.haveProgress) {
8934             Roo.MessageBox.hide();
8935         }
8936         
8937         
8938         var result = this.processResponse(response);
8939         if(result === true || result.success){
8940             this.form.afterAction(this, true);
8941             return;
8942         }
8943         if(result.errors){
8944             this.form.markInvalid(result.errors);
8945             this.failureType = Roo.form.Action.SERVER_INVALID;
8946         }
8947         this.form.afterAction(this, false);
8948     },
8949     failure : function(response)
8950     {
8951         this.uploadComplete= true;
8952         if (this.haveProgress) {
8953             Roo.MessageBox.hide();
8954         }
8955         
8956         this.response = response;
8957         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8958         this.form.afterAction(this, false);
8959     },
8960     
8961     handleResponse : function(response){
8962         if(this.form.errorReader){
8963             var rs = this.form.errorReader.read(response);
8964             var errors = [];
8965             if(rs.records){
8966                 for(var i = 0, len = rs.records.length; i < len; i++) {
8967                     var r = rs.records[i];
8968                     errors[i] = r.data;
8969                 }
8970             }
8971             if(errors.length < 1){
8972                 errors = null;
8973             }
8974             return {
8975                 success : rs.success,
8976                 errors : errors
8977             };
8978         }
8979         var ret = false;
8980         try {
8981             ret = Roo.decode(response.responseText);
8982         } catch (e) {
8983             ret = {
8984                 success: false,
8985                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8986                 errors : []
8987             };
8988         }
8989         return ret;
8990         
8991     }
8992 });
8993
8994
8995 Roo.form.Action.Load = function(form, options){
8996     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8997     this.reader = this.form.reader;
8998 };
8999
9000 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9001     type : 'load',
9002
9003     run : function(){
9004         
9005         Roo.Ajax.request(Roo.apply(
9006                 this.createCallback(), {
9007                     method:this.getMethod(),
9008                     url:this.getUrl(false),
9009                     params:this.getParams()
9010         }));
9011     },
9012
9013     success : function(response){
9014         
9015         var result = this.processResponse(response);
9016         if(result === true || !result.success || !result.data){
9017             this.failureType = Roo.form.Action.LOAD_FAILURE;
9018             this.form.afterAction(this, false);
9019             return;
9020         }
9021         this.form.clearInvalid();
9022         this.form.setValues(result.data);
9023         this.form.afterAction(this, true);
9024     },
9025
9026     handleResponse : function(response){
9027         if(this.form.reader){
9028             var rs = this.form.reader.read(response);
9029             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9030             return {
9031                 success : rs.success,
9032                 data : data
9033             };
9034         }
9035         return Roo.decode(response.responseText);
9036     }
9037 });
9038
9039 Roo.form.Action.ACTION_TYPES = {
9040     'load' : Roo.form.Action.Load,
9041     'submit' : Roo.form.Action.Submit
9042 };/*
9043  * - LGPL
9044  *
9045  * form
9046  *
9047  */
9048
9049 /**
9050  * @class Roo.bootstrap.Form
9051  * @extends Roo.bootstrap.Component
9052  * Bootstrap Form class
9053  * @cfg {String} method  GET | POST (default POST)
9054  * @cfg {String} labelAlign top | left (default top)
9055  * @cfg {String} align left  | right - for navbars
9056  * @cfg {Boolean} loadMask load mask when submit (default true)
9057
9058  *
9059  * @constructor
9060  * Create a new Form
9061  * @param {Object} config The config object
9062  */
9063
9064
9065 Roo.bootstrap.Form = function(config){
9066     
9067     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9068     
9069     Roo.bootstrap.Form.popover.apply();
9070     
9071     this.addEvents({
9072         /**
9073          * @event clientvalidation
9074          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9075          * @param {Form} this
9076          * @param {Boolean} valid true if the form has passed client-side validation
9077          */
9078         clientvalidation: true,
9079         /**
9080          * @event beforeaction
9081          * Fires before any action is performed. Return false to cancel the action.
9082          * @param {Form} this
9083          * @param {Action} action The action to be performed
9084          */
9085         beforeaction: true,
9086         /**
9087          * @event actionfailed
9088          * Fires when an action fails.
9089          * @param {Form} this
9090          * @param {Action} action The action that failed
9091          */
9092         actionfailed : true,
9093         /**
9094          * @event actioncomplete
9095          * Fires when an action is completed.
9096          * @param {Form} this
9097          * @param {Action} action The action that completed
9098          */
9099         actioncomplete : true
9100     });
9101 };
9102
9103 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9104
9105      /**
9106      * @cfg {String} method
9107      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9108      */
9109     method : 'POST',
9110     /**
9111      * @cfg {String} url
9112      * The URL to use for form actions if one isn't supplied in the action options.
9113      */
9114     /**
9115      * @cfg {Boolean} fileUpload
9116      * Set to true if this form is a file upload.
9117      */
9118
9119     /**
9120      * @cfg {Object} baseParams
9121      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9122      */
9123
9124     /**
9125      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9126      */
9127     timeout: 30,
9128     /**
9129      * @cfg {Sting} align (left|right) for navbar forms
9130      */
9131     align : 'left',
9132
9133     // private
9134     activeAction : null,
9135
9136     /**
9137      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9138      * element by passing it or its id or mask the form itself by passing in true.
9139      * @type Mixed
9140      */
9141     waitMsgTarget : false,
9142
9143     loadMask : true,
9144     
9145     /**
9146      * @cfg {Boolean} errorMask (true|false) default false
9147      */
9148     errorMask : false,
9149     
9150     /**
9151      * @cfg {Number} maskOffset Default 100
9152      */
9153     maskOffset : 100,
9154     
9155     /**
9156      * @cfg {Boolean} maskBody
9157      */
9158     maskBody : false,
9159
9160     getAutoCreate : function(){
9161
9162         var cfg = {
9163             tag: 'form',
9164             method : this.method || 'POST',
9165             id : this.id || Roo.id(),
9166             cls : ''
9167         };
9168         if (this.parent().xtype.match(/^Nav/)) {
9169             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9170
9171         }
9172
9173         if (this.labelAlign == 'left' ) {
9174             cfg.cls += ' form-horizontal';
9175         }
9176
9177
9178         return cfg;
9179     },
9180     initEvents : function()
9181     {
9182         this.el.on('submit', this.onSubmit, this);
9183         // this was added as random key presses on the form where triggering form submit.
9184         this.el.on('keypress', function(e) {
9185             if (e.getCharCode() != 13) {
9186                 return true;
9187             }
9188             // we might need to allow it for textareas.. and some other items.
9189             // check e.getTarget().
9190
9191             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9192                 return true;
9193             }
9194
9195             Roo.log("keypress blocked");
9196
9197             e.preventDefault();
9198             return false;
9199         });
9200         
9201     },
9202     // private
9203     onSubmit : function(e){
9204         e.stopEvent();
9205     },
9206
9207      /**
9208      * Returns true if client-side validation on the form is successful.
9209      * @return Boolean
9210      */
9211     isValid : function(){
9212         var items = this.getItems();
9213         var valid = true;
9214         var target = false;
9215         
9216         items.each(function(f){
9217             
9218             if(f.validate()){
9219                 return;
9220             }
9221             
9222             Roo.log('invalid field: ' + f.name);
9223             
9224             valid = false;
9225
9226             if(!target && f.el.isVisible(true)){
9227                 target = f;
9228             }
9229            
9230         });
9231         
9232         if(this.errorMask && !valid){
9233             Roo.bootstrap.Form.popover.mask(this, target);
9234         }
9235         
9236         return valid;
9237     },
9238     
9239     /**
9240      * Returns true if any fields in this form have changed since their original load.
9241      * @return Boolean
9242      */
9243     isDirty : function(){
9244         var dirty = false;
9245         var items = this.getItems();
9246         items.each(function(f){
9247            if(f.isDirty()){
9248                dirty = true;
9249                return false;
9250            }
9251            return true;
9252         });
9253         return dirty;
9254     },
9255      /**
9256      * Performs a predefined action (submit or load) or custom actions you define on this form.
9257      * @param {String} actionName The name of the action type
9258      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9259      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9260      * accept other config options):
9261      * <pre>
9262 Property          Type             Description
9263 ----------------  ---------------  ----------------------------------------------------------------------------------
9264 url               String           The url for the action (defaults to the form's url)
9265 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9266 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9267 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9268                                    validate the form on the client (defaults to false)
9269      * </pre>
9270      * @return {BasicForm} this
9271      */
9272     doAction : function(action, options){
9273         if(typeof action == 'string'){
9274             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9275         }
9276         if(this.fireEvent('beforeaction', this, action) !== false){
9277             this.beforeAction(action);
9278             action.run.defer(100, action);
9279         }
9280         return this;
9281     },
9282
9283     // private
9284     beforeAction : function(action){
9285         var o = action.options;
9286         
9287         if(this.loadMask){
9288             
9289             if(this.maskBody){
9290                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9291             } else {
9292                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9293             }
9294         }
9295         // not really supported yet.. ??
9296
9297         //if(this.waitMsgTarget === true){
9298         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9299         //}else if(this.waitMsgTarget){
9300         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9301         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9302         //}else {
9303         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9304        // }
9305
9306     },
9307
9308     // private
9309     afterAction : function(action, success){
9310         this.activeAction = null;
9311         var o = action.options;
9312
9313         if(this.loadMask){
9314             
9315             if(this.maskBody){
9316                 Roo.get(document.body).unmask();
9317             } else {
9318                 this.el.unmask();
9319             }
9320         }
9321         
9322         //if(this.waitMsgTarget === true){
9323 //            this.el.unmask();
9324         //}else if(this.waitMsgTarget){
9325         //    this.waitMsgTarget.unmask();
9326         //}else{
9327         //    Roo.MessageBox.updateProgress(1);
9328         //    Roo.MessageBox.hide();
9329        // }
9330         //
9331         if(success){
9332             if(o.reset){
9333                 this.reset();
9334             }
9335             Roo.callback(o.success, o.scope, [this, action]);
9336             this.fireEvent('actioncomplete', this, action);
9337
9338         }else{
9339
9340             // failure condition..
9341             // we have a scenario where updates need confirming.
9342             // eg. if a locking scenario exists..
9343             // we look for { errors : { needs_confirm : true }} in the response.
9344             if (
9345                 (typeof(action.result) != 'undefined')  &&
9346                 (typeof(action.result.errors) != 'undefined')  &&
9347                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9348            ){
9349                 var _t = this;
9350                 Roo.log("not supported yet");
9351                  /*
9352
9353                 Roo.MessageBox.confirm(
9354                     "Change requires confirmation",
9355                     action.result.errorMsg,
9356                     function(r) {
9357                         if (r != 'yes') {
9358                             return;
9359                         }
9360                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9361                     }
9362
9363                 );
9364                 */
9365
9366
9367                 return;
9368             }
9369
9370             Roo.callback(o.failure, o.scope, [this, action]);
9371             // show an error message if no failed handler is set..
9372             if (!this.hasListener('actionfailed')) {
9373                 Roo.log("need to add dialog support");
9374                 /*
9375                 Roo.MessageBox.alert("Error",
9376                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9377                         action.result.errorMsg :
9378                         "Saving Failed, please check your entries or try again"
9379                 );
9380                 */
9381             }
9382
9383             this.fireEvent('actionfailed', this, action);
9384         }
9385
9386     },
9387     /**
9388      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9389      * @param {String} id The value to search for
9390      * @return Field
9391      */
9392     findField : function(id){
9393         var items = this.getItems();
9394         var field = items.get(id);
9395         if(!field){
9396              items.each(function(f){
9397                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9398                     field = f;
9399                     return false;
9400                 }
9401                 return true;
9402             });
9403         }
9404         return field || null;
9405     },
9406      /**
9407      * Mark fields in this form invalid in bulk.
9408      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9409      * @return {BasicForm} this
9410      */
9411     markInvalid : function(errors){
9412         if(errors instanceof Array){
9413             for(var i = 0, len = errors.length; i < len; i++){
9414                 var fieldError = errors[i];
9415                 var f = this.findField(fieldError.id);
9416                 if(f){
9417                     f.markInvalid(fieldError.msg);
9418                 }
9419             }
9420         }else{
9421             var field, id;
9422             for(id in errors){
9423                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9424                     field.markInvalid(errors[id]);
9425                 }
9426             }
9427         }
9428         //Roo.each(this.childForms || [], function (f) {
9429         //    f.markInvalid(errors);
9430         //});
9431
9432         return this;
9433     },
9434
9435     /**
9436      * Set values for fields in this form in bulk.
9437      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9438      * @return {BasicForm} this
9439      */
9440     setValues : function(values){
9441         if(values instanceof Array){ // array of objects
9442             for(var i = 0, len = values.length; i < len; i++){
9443                 var v = values[i];
9444                 var f = this.findField(v.id);
9445                 if(f){
9446                     f.setValue(v.value);
9447                     if(this.trackResetOnLoad){
9448                         f.originalValue = f.getValue();
9449                     }
9450                 }
9451             }
9452         }else{ // object hash
9453             var field, id;
9454             for(id in values){
9455                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9456
9457                     if (field.setFromData &&
9458                         field.valueField &&
9459                         field.displayField &&
9460                         // combos' with local stores can
9461                         // be queried via setValue()
9462                         // to set their value..
9463                         (field.store && !field.store.isLocal)
9464                         ) {
9465                         // it's a combo
9466                         var sd = { };
9467                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9468                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9469                         field.setFromData(sd);
9470
9471                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9472                         
9473                         field.setFromData(values);
9474                         
9475                     } else {
9476                         field.setValue(values[id]);
9477                     }
9478
9479
9480                     if(this.trackResetOnLoad){
9481                         field.originalValue = field.getValue();
9482                     }
9483                 }
9484             }
9485         }
9486
9487         //Roo.each(this.childForms || [], function (f) {
9488         //    f.setValues(values);
9489         //});
9490
9491         return this;
9492     },
9493
9494     /**
9495      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9496      * they are returned as an array.
9497      * @param {Boolean} asString
9498      * @return {Object}
9499      */
9500     getValues : function(asString){
9501         //if (this.childForms) {
9502             // copy values from the child forms
9503         //    Roo.each(this.childForms, function (f) {
9504         //        this.setValues(f.getValues());
9505         //    }, this);
9506         //}
9507
9508
9509
9510         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9511         if(asString === true){
9512             return fs;
9513         }
9514         return Roo.urlDecode(fs);
9515     },
9516
9517     /**
9518      * Returns the fields in this form as an object with key/value pairs.
9519      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9520      * @return {Object}
9521      */
9522     getFieldValues : function(with_hidden)
9523     {
9524         var items = this.getItems();
9525         var ret = {};
9526         items.each(function(f){
9527             
9528             if (!f.getName()) {
9529                 return;
9530             }
9531             
9532             var v = f.getValue();
9533             
9534             if (f.inputType =='radio') {
9535                 if (typeof(ret[f.getName()]) == 'undefined') {
9536                     ret[f.getName()] = ''; // empty..
9537                 }
9538
9539                 if (!f.el.dom.checked) {
9540                     return;
9541
9542                 }
9543                 v = f.el.dom.value;
9544
9545             }
9546             
9547             if(f.xtype == 'MoneyField'){
9548                 ret[f.currencyName] = f.getCurrency();
9549             }
9550
9551             // not sure if this supported any more..
9552             if ((typeof(v) == 'object') && f.getRawValue) {
9553                 v = f.getRawValue() ; // dates..
9554             }
9555             // combo boxes where name != hiddenName...
9556             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9557                 ret[f.name] = f.getRawValue();
9558             }
9559             ret[f.getName()] = v;
9560         });
9561
9562         return ret;
9563     },
9564
9565     /**
9566      * Clears all invalid messages in this form.
9567      * @return {BasicForm} this
9568      */
9569     clearInvalid : function(){
9570         var items = this.getItems();
9571
9572         items.each(function(f){
9573            f.clearInvalid();
9574         });
9575
9576         return this;
9577     },
9578
9579     /**
9580      * Resets this form.
9581      * @return {BasicForm} this
9582      */
9583     reset : function(){
9584         var items = this.getItems();
9585         items.each(function(f){
9586             f.reset();
9587         });
9588
9589         Roo.each(this.childForms || [], function (f) {
9590             f.reset();
9591         });
9592
9593
9594         return this;
9595     },
9596     
9597     getItems : function()
9598     {
9599         var r=new Roo.util.MixedCollection(false, function(o){
9600             return o.id || (o.id = Roo.id());
9601         });
9602         var iter = function(el) {
9603             if (el.inputEl) {
9604                 r.add(el);
9605             }
9606             if (!el.items) {
9607                 return;
9608             }
9609             Roo.each(el.items,function(e) {
9610                 iter(e);
9611             });
9612         };
9613
9614         iter(this);
9615         return r;
9616     },
9617     
9618     hideFields : function(items)
9619     {
9620         Roo.each(items, function(i){
9621             
9622             var f = this.findField(i);
9623             
9624             if(!f){
9625                 return;
9626             }
9627             
9628             f.hide();
9629             
9630         }, this);
9631     },
9632     
9633     showFields : function(items)
9634     {
9635         Roo.each(items, function(i){
9636             
9637             var f = this.findField(i);
9638             
9639             if(!f){
9640                 return;
9641             }
9642             
9643             f.show();
9644             
9645         }, this);
9646     }
9647
9648 });
9649
9650 Roo.apply(Roo.bootstrap.Form, {
9651     
9652     popover : {
9653         
9654         padding : 5,
9655         
9656         isApplied : false,
9657         
9658         isMasked : false,
9659         
9660         form : false,
9661         
9662         target : false,
9663         
9664         toolTip : false,
9665         
9666         intervalID : false,
9667         
9668         maskEl : false,
9669         
9670         apply : function()
9671         {
9672             if(this.isApplied){
9673                 return;
9674             }
9675             
9676             this.maskEl = {
9677                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9678                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9679                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9680                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9681             };
9682             
9683             this.maskEl.top.enableDisplayMode("block");
9684             this.maskEl.left.enableDisplayMode("block");
9685             this.maskEl.bottom.enableDisplayMode("block");
9686             this.maskEl.right.enableDisplayMode("block");
9687             
9688             this.toolTip = new Roo.bootstrap.Tooltip({
9689                 cls : 'roo-form-error-popover',
9690                 alignment : {
9691                     'left' : ['r-l', [-2,0], 'right'],
9692                     'right' : ['l-r', [2,0], 'left'],
9693                     'bottom' : ['tl-bl', [0,2], 'top'],
9694                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9695                 }
9696             });
9697             
9698             this.toolTip.render(Roo.get(document.body));
9699
9700             this.toolTip.el.enableDisplayMode("block");
9701             
9702             Roo.get(document.body).on('click', function(){
9703                 this.unmask();
9704             }, this);
9705             
9706             Roo.get(document.body).on('touchstart', function(){
9707                 this.unmask();
9708             }, this);
9709             
9710             this.isApplied = true
9711         },
9712         
9713         mask : function(form, target)
9714         {
9715             this.form = form;
9716             
9717             this.target = target;
9718             
9719             if(!this.form.errorMask || !target.el){
9720                 return;
9721             }
9722             
9723             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9724             
9725             Roo.log(scrollable);
9726             
9727             var ot = this.target.el.calcOffsetsTo(scrollable);
9728             
9729             var scrollTo = ot[1] - this.form.maskOffset;
9730             
9731             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9732             
9733             scrollable.scrollTo('top', scrollTo);
9734             
9735             var box = this.target.el.getBox();
9736             Roo.log(box);
9737             var zIndex = Roo.bootstrap.Modal.zIndex++;
9738
9739             
9740             this.maskEl.top.setStyle('position', 'absolute');
9741             this.maskEl.top.setStyle('z-index', zIndex);
9742             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9743             this.maskEl.top.setLeft(0);
9744             this.maskEl.top.setTop(0);
9745             this.maskEl.top.show();
9746             
9747             this.maskEl.left.setStyle('position', 'absolute');
9748             this.maskEl.left.setStyle('z-index', zIndex);
9749             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9750             this.maskEl.left.setLeft(0);
9751             this.maskEl.left.setTop(box.y - this.padding);
9752             this.maskEl.left.show();
9753
9754             this.maskEl.bottom.setStyle('position', 'absolute');
9755             this.maskEl.bottom.setStyle('z-index', zIndex);
9756             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9757             this.maskEl.bottom.setLeft(0);
9758             this.maskEl.bottom.setTop(box.bottom + this.padding);
9759             this.maskEl.bottom.show();
9760
9761             this.maskEl.right.setStyle('position', 'absolute');
9762             this.maskEl.right.setStyle('z-index', zIndex);
9763             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9764             this.maskEl.right.setLeft(box.right + this.padding);
9765             this.maskEl.right.setTop(box.y - this.padding);
9766             this.maskEl.right.show();
9767
9768             this.toolTip.bindEl = this.target.el;
9769
9770             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9771
9772             var tip = this.target.blankText;
9773
9774             if(this.target.getValue() !== '' ) {
9775                 
9776                 if (this.target.invalidText.length) {
9777                     tip = this.target.invalidText;
9778                 } else if (this.target.regexText.length){
9779                     tip = this.target.regexText;
9780                 }
9781             }
9782
9783             this.toolTip.show(tip);
9784
9785             this.intervalID = window.setInterval(function() {
9786                 Roo.bootstrap.Form.popover.unmask();
9787             }, 10000);
9788
9789             window.onwheel = function(){ return false;};
9790             
9791             (function(){ this.isMasked = true; }).defer(500, this);
9792             
9793         },
9794         
9795         unmask : function()
9796         {
9797             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9798                 return;
9799             }
9800             
9801             this.maskEl.top.setStyle('position', 'absolute');
9802             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9803             this.maskEl.top.hide();
9804
9805             this.maskEl.left.setStyle('position', 'absolute');
9806             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9807             this.maskEl.left.hide();
9808
9809             this.maskEl.bottom.setStyle('position', 'absolute');
9810             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9811             this.maskEl.bottom.hide();
9812
9813             this.maskEl.right.setStyle('position', 'absolute');
9814             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9815             this.maskEl.right.hide();
9816             
9817             this.toolTip.hide();
9818             
9819             this.toolTip.el.hide();
9820             
9821             window.onwheel = function(){ return true;};
9822             
9823             if(this.intervalID){
9824                 window.clearInterval(this.intervalID);
9825                 this.intervalID = false;
9826             }
9827             
9828             this.isMasked = false;
9829             
9830         }
9831         
9832     }
9833     
9834 });
9835
9836 /*
9837  * Based on:
9838  * Ext JS Library 1.1.1
9839  * Copyright(c) 2006-2007, Ext JS, LLC.
9840  *
9841  * Originally Released Under LGPL - original licence link has changed is not relivant.
9842  *
9843  * Fork - LGPL
9844  * <script type="text/javascript">
9845  */
9846 /**
9847  * @class Roo.form.VTypes
9848  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9849  * @singleton
9850  */
9851 Roo.form.VTypes = function(){
9852     // closure these in so they are only created once.
9853     var alpha = /^[a-zA-Z_]+$/;
9854     var alphanum = /^[a-zA-Z0-9_]+$/;
9855     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9856     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9857
9858     // All these messages and functions are configurable
9859     return {
9860         /**
9861          * The function used to validate email addresses
9862          * @param {String} value The email address
9863          */
9864         'email' : function(v){
9865             return email.test(v);
9866         },
9867         /**
9868          * The error text to display when the email validation function returns false
9869          * @type String
9870          */
9871         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9872         /**
9873          * The keystroke filter mask to be applied on email input
9874          * @type RegExp
9875          */
9876         'emailMask' : /[a-z0-9_\.\-@]/i,
9877
9878         /**
9879          * The function used to validate URLs
9880          * @param {String} value The URL
9881          */
9882         'url' : function(v){
9883             return url.test(v);
9884         },
9885         /**
9886          * The error text to display when the url validation function returns false
9887          * @type String
9888          */
9889         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9890         
9891         /**
9892          * The function used to validate alpha values
9893          * @param {String} value The value
9894          */
9895         'alpha' : function(v){
9896             return alpha.test(v);
9897         },
9898         /**
9899          * The error text to display when the alpha validation function returns false
9900          * @type String
9901          */
9902         'alphaText' : 'This field should only contain letters and _',
9903         /**
9904          * The keystroke filter mask to be applied on alpha input
9905          * @type RegExp
9906          */
9907         'alphaMask' : /[a-z_]/i,
9908
9909         /**
9910          * The function used to validate alphanumeric values
9911          * @param {String} value The value
9912          */
9913         'alphanum' : function(v){
9914             return alphanum.test(v);
9915         },
9916         /**
9917          * The error text to display when the alphanumeric validation function returns false
9918          * @type String
9919          */
9920         'alphanumText' : 'This field should only contain letters, numbers and _',
9921         /**
9922          * The keystroke filter mask to be applied on alphanumeric input
9923          * @type RegExp
9924          */
9925         'alphanumMask' : /[a-z0-9_]/i
9926     };
9927 }();/*
9928  * - LGPL
9929  *
9930  * Input
9931  * 
9932  */
9933
9934 /**
9935  * @class Roo.bootstrap.Input
9936  * @extends Roo.bootstrap.Component
9937  * Bootstrap Input class
9938  * @cfg {Boolean} disabled is it disabled
9939  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9940  * @cfg {String} name name of the input
9941  * @cfg {string} fieldLabel - the label associated
9942  * @cfg {string} placeholder - placeholder to put in text.
9943  * @cfg {string}  before - input group add on before
9944  * @cfg {string} after - input group add on after
9945  * @cfg {string} size - (lg|sm) or leave empty..
9946  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9947  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9948  * @cfg {Number} md colspan out of 12 for computer-sized screens
9949  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9950  * @cfg {string} value default value of the input
9951  * @cfg {Number} labelWidth set the width of label 
9952  * @cfg {Number} labellg set the width of label (1-12)
9953  * @cfg {Number} labelmd set the width of label (1-12)
9954  * @cfg {Number} labelsm set the width of label (1-12)
9955  * @cfg {Number} labelxs set the width of label (1-12)
9956  * @cfg {String} labelAlign (top|left)
9957  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9958  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9959  * @cfg {String} indicatorpos (left|right) default left
9960  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9961  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9962
9963  * @cfg {String} align (left|center|right) Default left
9964  * @cfg {Boolean} forceFeedback (true|false) Default false
9965  * 
9966  * @constructor
9967  * Create a new Input
9968  * @param {Object} config The config object
9969  */
9970
9971 Roo.bootstrap.Input = function(config){
9972     
9973     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9974     
9975     this.addEvents({
9976         /**
9977          * @event focus
9978          * Fires when this field receives input focus.
9979          * @param {Roo.form.Field} this
9980          */
9981         focus : true,
9982         /**
9983          * @event blur
9984          * Fires when this field loses input focus.
9985          * @param {Roo.form.Field} this
9986          */
9987         blur : true,
9988         /**
9989          * @event specialkey
9990          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9991          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9992          * @param {Roo.form.Field} this
9993          * @param {Roo.EventObject} e The event object
9994          */
9995         specialkey : true,
9996         /**
9997          * @event change
9998          * Fires just before the field blurs if the field value has changed.
9999          * @param {Roo.form.Field} this
10000          * @param {Mixed} newValue The new value
10001          * @param {Mixed} oldValue The original value
10002          */
10003         change : true,
10004         /**
10005          * @event invalid
10006          * Fires after the field has been marked as invalid.
10007          * @param {Roo.form.Field} this
10008          * @param {String} msg The validation message
10009          */
10010         invalid : true,
10011         /**
10012          * @event valid
10013          * Fires after the field has been validated with no errors.
10014          * @param {Roo.form.Field} this
10015          */
10016         valid : true,
10017          /**
10018          * @event keyup
10019          * Fires after the key up
10020          * @param {Roo.form.Field} this
10021          * @param {Roo.EventObject}  e The event Object
10022          */
10023         keyup : true
10024     });
10025 };
10026
10027 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10028      /**
10029      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10030       automatic validation (defaults to "keyup").
10031      */
10032     validationEvent : "keyup",
10033      /**
10034      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10035      */
10036     validateOnBlur : true,
10037     /**
10038      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10039      */
10040     validationDelay : 250,
10041      /**
10042      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10043      */
10044     focusClass : "x-form-focus",  // not needed???
10045     
10046        
10047     /**
10048      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10049      */
10050     invalidClass : "has-warning",
10051     
10052     /**
10053      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10054      */
10055     validClass : "has-success",
10056     
10057     /**
10058      * @cfg {Boolean} hasFeedback (true|false) default true
10059      */
10060     hasFeedback : true,
10061     
10062     /**
10063      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10064      */
10065     invalidFeedbackClass : "glyphicon-warning-sign",
10066     
10067     /**
10068      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10069      */
10070     validFeedbackClass : "glyphicon-ok",
10071     
10072     /**
10073      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10074      */
10075     selectOnFocus : false,
10076     
10077      /**
10078      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10079      */
10080     maskRe : null,
10081        /**
10082      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10083      */
10084     vtype : null,
10085     
10086       /**
10087      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10088      */
10089     disableKeyFilter : false,
10090     
10091        /**
10092      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10093      */
10094     disabled : false,
10095      /**
10096      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10097      */
10098     allowBlank : true,
10099     /**
10100      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10101      */
10102     blankText : "Please complete this mandatory field",
10103     
10104      /**
10105      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10106      */
10107     minLength : 0,
10108     /**
10109      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10110      */
10111     maxLength : Number.MAX_VALUE,
10112     /**
10113      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10114      */
10115     minLengthText : "The minimum length for this field is {0}",
10116     /**
10117      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10118      */
10119     maxLengthText : "The maximum length for this field is {0}",
10120   
10121     
10122     /**
10123      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10124      * If available, this function will be called only after the basic validators all return true, and will be passed the
10125      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10126      */
10127     validator : null,
10128     /**
10129      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10130      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10131      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10132      */
10133     regex : null,
10134     /**
10135      * @cfg {String} regexText -- Depricated - use Invalid Text
10136      */
10137     regexText : "",
10138     
10139     /**
10140      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10141      */
10142     invalidText : "",
10143     
10144     
10145     
10146     autocomplete: false,
10147     
10148     
10149     fieldLabel : '',
10150     inputType : 'text',
10151     
10152     name : false,
10153     placeholder: false,
10154     before : false,
10155     after : false,
10156     size : false,
10157     hasFocus : false,
10158     preventMark: false,
10159     isFormField : true,
10160     value : '',
10161     labelWidth : 2,
10162     labelAlign : false,
10163     readOnly : false,
10164     align : false,
10165     formatedValue : false,
10166     forceFeedback : false,
10167     
10168     indicatorpos : 'left',
10169     
10170     labellg : 0,
10171     labelmd : 0,
10172     labelsm : 0,
10173     labelxs : 0,
10174     
10175     capture : '',
10176     accept : '',
10177     
10178     parentLabelAlign : function()
10179     {
10180         var parent = this;
10181         while (parent.parent()) {
10182             parent = parent.parent();
10183             if (typeof(parent.labelAlign) !='undefined') {
10184                 return parent.labelAlign;
10185             }
10186         }
10187         return 'left';
10188         
10189     },
10190     
10191     getAutoCreate : function()
10192     {
10193         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10194         
10195         var id = Roo.id();
10196         
10197         var cfg = {};
10198         
10199         if(this.inputType != 'hidden'){
10200             cfg.cls = 'form-group' //input-group
10201         }
10202         
10203         var input =  {
10204             tag: 'input',
10205             id : id,
10206             type : this.inputType,
10207             value : this.value,
10208             cls : 'form-control',
10209             placeholder : this.placeholder || '',
10210             autocomplete : this.autocomplete || 'new-password'
10211         };
10212         
10213         if(this.capture.length){
10214             input.capture = this.capture;
10215         }
10216         
10217         if(this.accept.length){
10218             input.accept = this.accept + "/*";
10219         }
10220         
10221         if(this.align){
10222             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10223         }
10224         
10225         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10226             input.maxLength = this.maxLength;
10227         }
10228         
10229         if (this.disabled) {
10230             input.disabled=true;
10231         }
10232         
10233         if (this.readOnly) {
10234             input.readonly=true;
10235         }
10236         
10237         if (this.name) {
10238             input.name = this.name;
10239         }
10240         
10241         if (this.size) {
10242             input.cls += ' input-' + this.size;
10243         }
10244         
10245         var settings=this;
10246         ['xs','sm','md','lg'].map(function(size){
10247             if (settings[size]) {
10248                 cfg.cls += ' col-' + size + '-' + settings[size];
10249             }
10250         });
10251         
10252         var inputblock = input;
10253         
10254         var feedback = {
10255             tag: 'span',
10256             cls: 'glyphicon form-control-feedback'
10257         };
10258             
10259         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10260             
10261             inputblock = {
10262                 cls : 'has-feedback',
10263                 cn :  [
10264                     input,
10265                     feedback
10266                 ] 
10267             };  
10268         }
10269         
10270         if (this.before || this.after) {
10271             
10272             inputblock = {
10273                 cls : 'input-group',
10274                 cn :  [] 
10275             };
10276             
10277             if (this.before && typeof(this.before) == 'string') {
10278                 
10279                 inputblock.cn.push({
10280                     tag :'span',
10281                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10282                     html : this.before
10283                 });
10284             }
10285             if (this.before && typeof(this.before) == 'object') {
10286                 this.before = Roo.factory(this.before);
10287                 
10288                 inputblock.cn.push({
10289                     tag :'span',
10290                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10291                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10292                 });
10293             }
10294             
10295             inputblock.cn.push(input);
10296             
10297             if (this.after && typeof(this.after) == 'string') {
10298                 inputblock.cn.push({
10299                     tag :'span',
10300                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10301                     html : this.after
10302                 });
10303             }
10304             if (this.after && typeof(this.after) == 'object') {
10305                 this.after = Roo.factory(this.after);
10306                 
10307                 inputblock.cn.push({
10308                     tag :'span',
10309                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10310                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10311                 });
10312             }
10313             
10314             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10315                 inputblock.cls += ' has-feedback';
10316                 inputblock.cn.push(feedback);
10317             }
10318         };
10319         var indicator = {
10320             tag : 'i',
10321             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10322             tooltip : 'This field is required'
10323         };
10324         if (Roo.bootstrap.version == 4) {
10325             indicator = {
10326                 tag : 'i',
10327                 style : 'display-none'
10328             };
10329         }
10330         if (align ==='left' && this.fieldLabel.length) {
10331             
10332             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10333             
10334             cfg.cn = [
10335                 indicator,
10336                 {
10337                     tag: 'label',
10338                     'for' :  id,
10339                     cls : 'control-label col-form-label',
10340                     html : this.fieldLabel
10341
10342                 },
10343                 {
10344                     cls : "", 
10345                     cn: [
10346                         inputblock
10347                     ]
10348                 }
10349             ];
10350             
10351             var labelCfg = cfg.cn[1];
10352             var contentCfg = cfg.cn[2];
10353             
10354             if(this.indicatorpos == 'right'){
10355                 cfg.cn = [
10356                     {
10357                         tag: 'label',
10358                         'for' :  id,
10359                         cls : 'control-label col-form-label',
10360                         cn : [
10361                             {
10362                                 tag : 'span',
10363                                 html : this.fieldLabel
10364                             },
10365                             indicator
10366                         ]
10367                     },
10368                     {
10369                         cls : "",
10370                         cn: [
10371                             inputblock
10372                         ]
10373                     }
10374
10375                 ];
10376                 
10377                 labelCfg = cfg.cn[0];
10378                 contentCfg = cfg.cn[1];
10379             
10380             }
10381             
10382             if(this.labelWidth > 12){
10383                 labelCfg.style = "width: " + this.labelWidth + 'px';
10384             }
10385             
10386             if(this.labelWidth < 13 && this.labelmd == 0){
10387                 this.labelmd = this.labelWidth;
10388             }
10389             
10390             if(this.labellg > 0){
10391                 labelCfg.cls += ' col-lg-' + this.labellg;
10392                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10393             }
10394             
10395             if(this.labelmd > 0){
10396                 labelCfg.cls += ' col-md-' + this.labelmd;
10397                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10398             }
10399             
10400             if(this.labelsm > 0){
10401                 labelCfg.cls += ' col-sm-' + this.labelsm;
10402                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10403             }
10404             
10405             if(this.labelxs > 0){
10406                 labelCfg.cls += ' col-xs-' + this.labelxs;
10407                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10408             }
10409             
10410             
10411         } else if ( this.fieldLabel.length) {
10412                 
10413             cfg.cn = [
10414                 {
10415                     tag : 'i',
10416                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10417                     tooltip : 'This field is required'
10418                 },
10419                 {
10420                     tag: 'label',
10421                    //cls : 'input-group-addon',
10422                     html : this.fieldLabel
10423
10424                 },
10425
10426                inputblock
10427
10428            ];
10429            
10430            if(this.indicatorpos == 'right'){
10431                 
10432                 cfg.cn = [
10433                     {
10434                         tag: 'label',
10435                        //cls : 'input-group-addon',
10436                         html : this.fieldLabel
10437
10438                     },
10439                     {
10440                         tag : 'i',
10441                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10442                         tooltip : 'This field is required'
10443                     },
10444
10445                    inputblock
10446
10447                ];
10448
10449             }
10450
10451         } else {
10452             
10453             cfg.cn = [
10454
10455                     inputblock
10456
10457             ];
10458                 
10459                 
10460         };
10461         
10462         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10463            cfg.cls += ' navbar-form';
10464         }
10465         
10466         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10467             // on BS4 we do this only if not form 
10468             cfg.cls += ' navbar-form';
10469             cfg.tag = 'li';
10470         }
10471         
10472         return cfg;
10473         
10474     },
10475     /**
10476      * return the real input element.
10477      */
10478     inputEl: function ()
10479     {
10480         return this.el.select('input.form-control',true).first();
10481     },
10482     
10483     tooltipEl : function()
10484     {
10485         return this.inputEl();
10486     },
10487     
10488     indicatorEl : function()
10489     {
10490         if (Roo.bootstrap.version == 4) {
10491             return false; // not enabled in v4 yet.
10492         }
10493         
10494         var indicator = this.el.select('i.roo-required-indicator',true).first();
10495         
10496         if(!indicator){
10497             return false;
10498         }
10499         
10500         return indicator;
10501         
10502     },
10503     
10504     setDisabled : function(v)
10505     {
10506         var i  = this.inputEl().dom;
10507         if (!v) {
10508             i.removeAttribute('disabled');
10509             return;
10510             
10511         }
10512         i.setAttribute('disabled','true');
10513     },
10514     initEvents : function()
10515     {
10516           
10517         this.inputEl().on("keydown" , this.fireKey,  this);
10518         this.inputEl().on("focus", this.onFocus,  this);
10519         this.inputEl().on("blur", this.onBlur,  this);
10520         
10521         this.inputEl().relayEvent('keyup', this);
10522         
10523         this.indicator = this.indicatorEl();
10524         
10525         if(this.indicator){
10526             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10527         }
10528  
10529         // reference to original value for reset
10530         this.originalValue = this.getValue();
10531         //Roo.form.TextField.superclass.initEvents.call(this);
10532         if(this.validationEvent == 'keyup'){
10533             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10534             this.inputEl().on('keyup', this.filterValidation, this);
10535         }
10536         else if(this.validationEvent !== false){
10537             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10538         }
10539         
10540         if(this.selectOnFocus){
10541             this.on("focus", this.preFocus, this);
10542             
10543         }
10544         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10545             this.inputEl().on("keypress", this.filterKeys, this);
10546         } else {
10547             this.inputEl().relayEvent('keypress', this);
10548         }
10549        /* if(this.grow){
10550             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10551             this.el.on("click", this.autoSize,  this);
10552         }
10553         */
10554         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10555             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10556         }
10557         
10558         if (typeof(this.before) == 'object') {
10559             this.before.render(this.el.select('.roo-input-before',true).first());
10560         }
10561         if (typeof(this.after) == 'object') {
10562             this.after.render(this.el.select('.roo-input-after',true).first());
10563         }
10564         
10565         this.inputEl().on('change', this.onChange, this);
10566         
10567     },
10568     filterValidation : function(e){
10569         if(!e.isNavKeyPress()){
10570             this.validationTask.delay(this.validationDelay);
10571         }
10572     },
10573      /**
10574      * Validates the field value
10575      * @return {Boolean} True if the value is valid, else false
10576      */
10577     validate : function(){
10578         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10579         if(this.disabled || this.validateValue(this.getRawValue())){
10580             this.markValid();
10581             return true;
10582         }
10583         
10584         this.markInvalid();
10585         return false;
10586     },
10587     
10588     
10589     /**
10590      * Validates a value according to the field's validation rules and marks the field as invalid
10591      * if the validation fails
10592      * @param {Mixed} value The value to validate
10593      * @return {Boolean} True if the value is valid, else false
10594      */
10595     validateValue : function(value)
10596     {
10597         if(this.getVisibilityEl().hasClass('hidden')){
10598             return true;
10599         }
10600         
10601         if(value.length < 1)  { // if it's blank
10602             if(this.allowBlank){
10603                 return true;
10604             }
10605             return false;
10606         }
10607         
10608         if(value.length < this.minLength){
10609             return false;
10610         }
10611         if(value.length > this.maxLength){
10612             return false;
10613         }
10614         if(this.vtype){
10615             var vt = Roo.form.VTypes;
10616             if(!vt[this.vtype](value, this)){
10617                 return false;
10618             }
10619         }
10620         if(typeof this.validator == "function"){
10621             var msg = this.validator(value);
10622             if(msg !== true){
10623                 return false;
10624             }
10625             if (typeof(msg) == 'string') {
10626                 this.invalidText = msg;
10627             }
10628         }
10629         
10630         if(this.regex && !this.regex.test(value)){
10631             return false;
10632         }
10633         
10634         return true;
10635     },
10636     
10637      // private
10638     fireKey : function(e){
10639         //Roo.log('field ' + e.getKey());
10640         if(e.isNavKeyPress()){
10641             this.fireEvent("specialkey", this, e);
10642         }
10643     },
10644     focus : function (selectText){
10645         if(this.rendered){
10646             this.inputEl().focus();
10647             if(selectText === true){
10648                 this.inputEl().dom.select();
10649             }
10650         }
10651         return this;
10652     } ,
10653     
10654     onFocus : function(){
10655         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10656            // this.el.addClass(this.focusClass);
10657         }
10658         if(!this.hasFocus){
10659             this.hasFocus = true;
10660             this.startValue = this.getValue();
10661             this.fireEvent("focus", this);
10662         }
10663     },
10664     
10665     beforeBlur : Roo.emptyFn,
10666
10667     
10668     // private
10669     onBlur : function(){
10670         this.beforeBlur();
10671         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10672             //this.el.removeClass(this.focusClass);
10673         }
10674         this.hasFocus = false;
10675         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10676             this.validate();
10677         }
10678         var v = this.getValue();
10679         if(String(v) !== String(this.startValue)){
10680             this.fireEvent('change', this, v, this.startValue);
10681         }
10682         this.fireEvent("blur", this);
10683     },
10684     
10685     onChange : function(e)
10686     {
10687         var v = this.getValue();
10688         if(String(v) !== String(this.startValue)){
10689             this.fireEvent('change', this, v, this.startValue);
10690         }
10691         
10692     },
10693     
10694     /**
10695      * Resets the current field value to the originally loaded value and clears any validation messages
10696      */
10697     reset : function(){
10698         this.setValue(this.originalValue);
10699         this.validate();
10700     },
10701      /**
10702      * Returns the name of the field
10703      * @return {Mixed} name The name field
10704      */
10705     getName: function(){
10706         return this.name;
10707     },
10708      /**
10709      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10710      * @return {Mixed} value The field value
10711      */
10712     getValue : function(){
10713         
10714         var v = this.inputEl().getValue();
10715         
10716         return v;
10717     },
10718     /**
10719      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10720      * @return {Mixed} value The field value
10721      */
10722     getRawValue : function(){
10723         var v = this.inputEl().getValue();
10724         
10725         return v;
10726     },
10727     
10728     /**
10729      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10730      * @param {Mixed} value The value to set
10731      */
10732     setRawValue : function(v){
10733         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10734     },
10735     
10736     selectText : function(start, end){
10737         var v = this.getRawValue();
10738         if(v.length > 0){
10739             start = start === undefined ? 0 : start;
10740             end = end === undefined ? v.length : end;
10741             var d = this.inputEl().dom;
10742             if(d.setSelectionRange){
10743                 d.setSelectionRange(start, end);
10744             }else if(d.createTextRange){
10745                 var range = d.createTextRange();
10746                 range.moveStart("character", start);
10747                 range.moveEnd("character", v.length-end);
10748                 range.select();
10749             }
10750         }
10751     },
10752     
10753     /**
10754      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10755      * @param {Mixed} value The value to set
10756      */
10757     setValue : function(v){
10758         this.value = v;
10759         if(this.rendered){
10760             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10761             this.validate();
10762         }
10763     },
10764     
10765     /*
10766     processValue : function(value){
10767         if(this.stripCharsRe){
10768             var newValue = value.replace(this.stripCharsRe, '');
10769             if(newValue !== value){
10770                 this.setRawValue(newValue);
10771                 return newValue;
10772             }
10773         }
10774         return value;
10775     },
10776   */
10777     preFocus : function(){
10778         
10779         if(this.selectOnFocus){
10780             this.inputEl().dom.select();
10781         }
10782     },
10783     filterKeys : function(e){
10784         var k = e.getKey();
10785         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10786             return;
10787         }
10788         var c = e.getCharCode(), cc = String.fromCharCode(c);
10789         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10790             return;
10791         }
10792         if(!this.maskRe.test(cc)){
10793             e.stopEvent();
10794         }
10795     },
10796      /**
10797      * Clear any invalid styles/messages for this field
10798      */
10799     clearInvalid : function(){
10800         
10801         if(!this.el || this.preventMark){ // not rendered
10802             return;
10803         }
10804         
10805         
10806         this.el.removeClass([this.invalidClass, 'is-invalid']);
10807         
10808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10809             
10810             var feedback = this.el.select('.form-control-feedback', true).first();
10811             
10812             if(feedback){
10813                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10814             }
10815             
10816         }
10817         
10818         if(this.indicator){
10819             this.indicator.removeClass('visible');
10820             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10821         }
10822         
10823         this.fireEvent('valid', this);
10824     },
10825     
10826      /**
10827      * Mark this field as valid
10828      */
10829     markValid : function()
10830     {
10831         if(!this.el  || this.preventMark){ // not rendered...
10832             return;
10833         }
10834         
10835         this.el.removeClass([this.invalidClass, this.validClass]);
10836         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10837
10838         var feedback = this.el.select('.form-control-feedback', true).first();
10839             
10840         if(feedback){
10841             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10842         }
10843         
10844         if(this.indicator){
10845             this.indicator.removeClass('visible');
10846             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10847         }
10848         
10849         if(this.disabled){
10850             return;
10851         }
10852         
10853         if(this.allowBlank && !this.getRawValue().length){
10854             return;
10855         }
10856         if (Roo.bootstrap.version == 3) {
10857             this.el.addClass(this.validClass);
10858         } else {
10859             this.inputEl().addClass('is-valid');
10860         }
10861
10862         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10863             
10864             var feedback = this.el.select('.form-control-feedback', true).first();
10865             
10866             if(feedback){
10867                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10868                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10869             }
10870             
10871         }
10872         
10873         this.fireEvent('valid', this);
10874     },
10875     
10876      /**
10877      * Mark this field as invalid
10878      * @param {String} msg The validation message
10879      */
10880     markInvalid : function(msg)
10881     {
10882         if(!this.el  || this.preventMark){ // not rendered
10883             return;
10884         }
10885         
10886         this.el.removeClass([this.invalidClass, this.validClass]);
10887         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10888         
10889         var feedback = this.el.select('.form-control-feedback', true).first();
10890             
10891         if(feedback){
10892             this.el.select('.form-control-feedback', true).first().removeClass(
10893                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10894         }
10895
10896         if(this.disabled){
10897             return;
10898         }
10899         
10900         if(this.allowBlank && !this.getRawValue().length){
10901             return;
10902         }
10903         
10904         if(this.indicator){
10905             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10906             this.indicator.addClass('visible');
10907         }
10908         if (Roo.bootstrap.version == 3) {
10909             this.el.addClass(this.invalidClass);
10910         } else {
10911             this.inputEl().addClass('is-invalid');
10912         }
10913         
10914         
10915         
10916         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10917             
10918             var feedback = this.el.select('.form-control-feedback', true).first();
10919             
10920             if(feedback){
10921                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10922                 
10923                 if(this.getValue().length || this.forceFeedback){
10924                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10925                 }
10926                 
10927             }
10928             
10929         }
10930         
10931         this.fireEvent('invalid', this, msg);
10932     },
10933     // private
10934     SafariOnKeyDown : function(event)
10935     {
10936         // this is a workaround for a password hang bug on chrome/ webkit.
10937         if (this.inputEl().dom.type != 'password') {
10938             return;
10939         }
10940         
10941         var isSelectAll = false;
10942         
10943         if(this.inputEl().dom.selectionEnd > 0){
10944             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10945         }
10946         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10947             event.preventDefault();
10948             this.setValue('');
10949             return;
10950         }
10951         
10952         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10953             
10954             event.preventDefault();
10955             // this is very hacky as keydown always get's upper case.
10956             //
10957             var cc = String.fromCharCode(event.getCharCode());
10958             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10959             
10960         }
10961     },
10962     adjustWidth : function(tag, w){
10963         tag = tag.toLowerCase();
10964         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10965             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10966                 if(tag == 'input'){
10967                     return w + 2;
10968                 }
10969                 if(tag == 'textarea'){
10970                     return w-2;
10971                 }
10972             }else if(Roo.isOpera){
10973                 if(tag == 'input'){
10974                     return w + 2;
10975                 }
10976                 if(tag == 'textarea'){
10977                     return w-2;
10978                 }
10979             }
10980         }
10981         return w;
10982     },
10983     
10984     setFieldLabel : function(v)
10985     {
10986         if(!this.rendered){
10987             return;
10988         }
10989         
10990         if(this.indicatorEl()){
10991             var ar = this.el.select('label > span',true);
10992             
10993             if (ar.elements.length) {
10994                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10995                 this.fieldLabel = v;
10996                 return;
10997             }
10998             
10999             var br = this.el.select('label',true);
11000             
11001             if(br.elements.length) {
11002                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11003                 this.fieldLabel = v;
11004                 return;
11005             }
11006             
11007             Roo.log('Cannot Found any of label > span || label in input');
11008             return;
11009         }
11010         
11011         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11012         this.fieldLabel = v;
11013         
11014         
11015     }
11016 });
11017
11018  
11019 /*
11020  * - LGPL
11021  *
11022  * Input
11023  * 
11024  */
11025
11026 /**
11027  * @class Roo.bootstrap.TextArea
11028  * @extends Roo.bootstrap.Input
11029  * Bootstrap TextArea class
11030  * @cfg {Number} cols Specifies the visible width of a text area
11031  * @cfg {Number} rows Specifies the visible number of lines in a text area
11032  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11033  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11034  * @cfg {string} html text
11035  * 
11036  * @constructor
11037  * Create a new TextArea
11038  * @param {Object} config The config object
11039  */
11040
11041 Roo.bootstrap.TextArea = function(config){
11042     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11043    
11044 };
11045
11046 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11047      
11048     cols : false,
11049     rows : 5,
11050     readOnly : false,
11051     warp : 'soft',
11052     resize : false,
11053     value: false,
11054     html: false,
11055     
11056     getAutoCreate : function(){
11057         
11058         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11059         
11060         var id = Roo.id();
11061         
11062         var cfg = {};
11063         
11064         if(this.inputType != 'hidden'){
11065             cfg.cls = 'form-group' //input-group
11066         }
11067         
11068         var input =  {
11069             tag: 'textarea',
11070             id : id,
11071             warp : this.warp,
11072             rows : this.rows,
11073             value : this.value || '',
11074             html: this.html || '',
11075             cls : 'form-control',
11076             placeholder : this.placeholder || '' 
11077             
11078         };
11079         
11080         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11081             input.maxLength = this.maxLength;
11082         }
11083         
11084         if(this.resize){
11085             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11086         }
11087         
11088         if(this.cols){
11089             input.cols = this.cols;
11090         }
11091         
11092         if (this.readOnly) {
11093             input.readonly = true;
11094         }
11095         
11096         if (this.name) {
11097             input.name = this.name;
11098         }
11099         
11100         if (this.size) {
11101             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11102         }
11103         
11104         var settings=this;
11105         ['xs','sm','md','lg'].map(function(size){
11106             if (settings[size]) {
11107                 cfg.cls += ' col-' + size + '-' + settings[size];
11108             }
11109         });
11110         
11111         var inputblock = input;
11112         
11113         if(this.hasFeedback && !this.allowBlank){
11114             
11115             var feedback = {
11116                 tag: 'span',
11117                 cls: 'glyphicon form-control-feedback'
11118             };
11119
11120             inputblock = {
11121                 cls : 'has-feedback',
11122                 cn :  [
11123                     input,
11124                     feedback
11125                 ] 
11126             };  
11127         }
11128         
11129         
11130         if (this.before || this.after) {
11131             
11132             inputblock = {
11133                 cls : 'input-group',
11134                 cn :  [] 
11135             };
11136             if (this.before) {
11137                 inputblock.cn.push({
11138                     tag :'span',
11139                     cls : 'input-group-addon',
11140                     html : this.before
11141                 });
11142             }
11143             
11144             inputblock.cn.push(input);
11145             
11146             if(this.hasFeedback && !this.allowBlank){
11147                 inputblock.cls += ' has-feedback';
11148                 inputblock.cn.push(feedback);
11149             }
11150             
11151             if (this.after) {
11152                 inputblock.cn.push({
11153                     tag :'span',
11154                     cls : 'input-group-addon',
11155                     html : this.after
11156                 });
11157             }
11158             
11159         }
11160         
11161         if (align ==='left' && this.fieldLabel.length) {
11162             cfg.cn = [
11163                 {
11164                     tag: 'label',
11165                     'for' :  id,
11166                     cls : 'control-label',
11167                     html : this.fieldLabel
11168                 },
11169                 {
11170                     cls : "",
11171                     cn: [
11172                         inputblock
11173                     ]
11174                 }
11175
11176             ];
11177             
11178             if(this.labelWidth > 12){
11179                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11180             }
11181
11182             if(this.labelWidth < 13 && this.labelmd == 0){
11183                 this.labelmd = this.labelWidth;
11184             }
11185
11186             if(this.labellg > 0){
11187                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11188                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11189             }
11190
11191             if(this.labelmd > 0){
11192                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11193                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11194             }
11195
11196             if(this.labelsm > 0){
11197                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11198                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11199             }
11200
11201             if(this.labelxs > 0){
11202                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11203                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11204             }
11205             
11206         } else if ( this.fieldLabel.length) {
11207             cfg.cn = [
11208
11209                {
11210                    tag: 'label',
11211                    //cls : 'input-group-addon',
11212                    html : this.fieldLabel
11213
11214                },
11215
11216                inputblock
11217
11218            ];
11219
11220         } else {
11221
11222             cfg.cn = [
11223
11224                 inputblock
11225
11226             ];
11227                 
11228         }
11229         
11230         if (this.disabled) {
11231             input.disabled=true;
11232         }
11233         
11234         return cfg;
11235         
11236     },
11237     /**
11238      * return the real textarea element.
11239      */
11240     inputEl: function ()
11241     {
11242         return this.el.select('textarea.form-control',true).first();
11243     },
11244     
11245     /**
11246      * Clear any invalid styles/messages for this field
11247      */
11248     clearInvalid : function()
11249     {
11250         
11251         if(!this.el || this.preventMark){ // not rendered
11252             return;
11253         }
11254         
11255         var label = this.el.select('label', true).first();
11256         var icon = this.el.select('i.fa-star', true).first();
11257         
11258         if(label && icon){
11259             icon.remove();
11260         }
11261         this.el.removeClass( this.validClass);
11262         this.inputEl().removeClass('is-invalid');
11263          
11264         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11265             
11266             var feedback = this.el.select('.form-control-feedback', true).first();
11267             
11268             if(feedback){
11269                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11270             }
11271             
11272         }
11273         
11274         this.fireEvent('valid', this);
11275     },
11276     
11277      /**
11278      * Mark this field as valid
11279      */
11280     markValid : function()
11281     {
11282         if(!this.el  || this.preventMark){ // not rendered
11283             return;
11284         }
11285         
11286         this.el.removeClass([this.invalidClass, this.validClass]);
11287         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11288         
11289         var feedback = this.el.select('.form-control-feedback', true).first();
11290             
11291         if(feedback){
11292             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11293         }
11294
11295         if(this.disabled || this.allowBlank){
11296             return;
11297         }
11298         
11299         var label = this.el.select('label', true).first();
11300         var icon = this.el.select('i.fa-star', true).first();
11301         
11302         if(label && icon){
11303             icon.remove();
11304         }
11305         if (Roo.bootstrap.version == 3) {
11306             this.el.addClass(this.validClass);
11307         } else {
11308             this.inputEl().addClass('is-valid');
11309         }
11310         
11311         
11312         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11313             
11314             var feedback = this.el.select('.form-control-feedback', true).first();
11315             
11316             if(feedback){
11317                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11318                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11319             }
11320             
11321         }
11322         
11323         this.fireEvent('valid', this);
11324     },
11325     
11326      /**
11327      * Mark this field as invalid
11328      * @param {String} msg The validation message
11329      */
11330     markInvalid : function(msg)
11331     {
11332         if(!this.el  || this.preventMark){ // not rendered
11333             return;
11334         }
11335         
11336         this.el.removeClass([this.invalidClass, this.validClass]);
11337         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11338         
11339         var feedback = this.el.select('.form-control-feedback', true).first();
11340             
11341         if(feedback){
11342             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11343         }
11344
11345         if(this.disabled || this.allowBlank){
11346             return;
11347         }
11348         
11349         var label = this.el.select('label', true).first();
11350         var icon = this.el.select('i.fa-star', true).first();
11351         
11352         if(!this.getValue().length && label && !icon){
11353             this.el.createChild({
11354                 tag : 'i',
11355                 cls : 'text-danger fa fa-lg fa-star',
11356                 tooltip : 'This field is required',
11357                 style : 'margin-right:5px;'
11358             }, label, true);
11359         }
11360         
11361         if (Roo.bootstrap.version == 3) {
11362             this.el.addClass(this.invalidClass);
11363         } else {
11364             this.inputEl().addClass('is-invalid');
11365         }
11366         
11367         // fixme ... this may be depricated need to test..
11368         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11369             
11370             var feedback = this.el.select('.form-control-feedback', true).first();
11371             
11372             if(feedback){
11373                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11374                 
11375                 if(this.getValue().length || this.forceFeedback){
11376                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11377                 }
11378                 
11379             }
11380             
11381         }
11382         
11383         this.fireEvent('invalid', this, msg);
11384     }
11385 });
11386
11387  
11388 /*
11389  * - LGPL
11390  *
11391  * trigger field - base class for combo..
11392  * 
11393  */
11394  
11395 /**
11396  * @class Roo.bootstrap.TriggerField
11397  * @extends Roo.bootstrap.Input
11398  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11399  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11400  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11401  * for which you can provide a custom implementation.  For example:
11402  * <pre><code>
11403 var trigger = new Roo.bootstrap.TriggerField();
11404 trigger.onTriggerClick = myTriggerFn;
11405 trigger.applyTo('my-field');
11406 </code></pre>
11407  *
11408  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11409  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11410  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11411  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11412  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11413
11414  * @constructor
11415  * Create a new TriggerField.
11416  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11417  * to the base TextField)
11418  */
11419 Roo.bootstrap.TriggerField = function(config){
11420     this.mimicing = false;
11421     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11422 };
11423
11424 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11425     /**
11426      * @cfg {String} triggerClass A CSS class to apply to the trigger
11427      */
11428      /**
11429      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11430      */
11431     hideTrigger:false,
11432
11433     /**
11434      * @cfg {Boolean} removable (true|false) special filter default false
11435      */
11436     removable : false,
11437     
11438     /** @cfg {Boolean} grow @hide */
11439     /** @cfg {Number} growMin @hide */
11440     /** @cfg {Number} growMax @hide */
11441
11442     /**
11443      * @hide 
11444      * @method
11445      */
11446     autoSize: Roo.emptyFn,
11447     // private
11448     monitorTab : true,
11449     // private
11450     deferHeight : true,
11451
11452     
11453     actionMode : 'wrap',
11454     
11455     caret : false,
11456     
11457     
11458     getAutoCreate : function(){
11459        
11460         var align = this.labelAlign || this.parentLabelAlign();
11461         
11462         var id = Roo.id();
11463         
11464         var cfg = {
11465             cls: 'form-group' //input-group
11466         };
11467         
11468         
11469         var input =  {
11470             tag: 'input',
11471             id : id,
11472             type : this.inputType,
11473             cls : 'form-control',
11474             autocomplete: 'new-password',
11475             placeholder : this.placeholder || '' 
11476             
11477         };
11478         if (this.name) {
11479             input.name = this.name;
11480         }
11481         if (this.size) {
11482             input.cls += ' input-' + this.size;
11483         }
11484         
11485         if (this.disabled) {
11486             input.disabled=true;
11487         }
11488         
11489         var inputblock = input;
11490         
11491         if(this.hasFeedback && !this.allowBlank){
11492             
11493             var feedback = {
11494                 tag: 'span',
11495                 cls: 'glyphicon form-control-feedback'
11496             };
11497             
11498             if(this.removable && !this.editable && !this.tickable){
11499                 inputblock = {
11500                     cls : 'has-feedback',
11501                     cn :  [
11502                         inputblock,
11503                         {
11504                             tag: 'button',
11505                             html : 'x',
11506                             cls : 'roo-combo-removable-btn close'
11507                         },
11508                         feedback
11509                     ] 
11510                 };
11511             } else {
11512                 inputblock = {
11513                     cls : 'has-feedback',
11514                     cn :  [
11515                         inputblock,
11516                         feedback
11517                     ] 
11518                 };
11519             }
11520
11521         } else {
11522             if(this.removable && !this.editable && !this.tickable){
11523                 inputblock = {
11524                     cls : 'roo-removable',
11525                     cn :  [
11526                         inputblock,
11527                         {
11528                             tag: 'button',
11529                             html : 'x',
11530                             cls : 'roo-combo-removable-btn close'
11531                         }
11532                     ] 
11533                 };
11534             }
11535         }
11536         
11537         if (this.before || this.after) {
11538             
11539             inputblock = {
11540                 cls : 'input-group',
11541                 cn :  [] 
11542             };
11543             if (this.before) {
11544                 inputblock.cn.push({
11545                     tag :'span',
11546                     cls : 'input-group-addon input-group-prepend input-group-text',
11547                     html : this.before
11548                 });
11549             }
11550             
11551             inputblock.cn.push(input);
11552             
11553             if(this.hasFeedback && !this.allowBlank){
11554                 inputblock.cls += ' has-feedback';
11555                 inputblock.cn.push(feedback);
11556             }
11557             
11558             if (this.after) {
11559                 inputblock.cn.push({
11560                     tag :'span',
11561                     cls : 'input-group-addon input-group-append input-group-text',
11562                     html : this.after
11563                 });
11564             }
11565             
11566         };
11567         
11568       
11569         
11570         var ibwrap = inputblock;
11571         
11572         if(this.multiple){
11573             ibwrap = {
11574                 tag: 'ul',
11575                 cls: 'roo-select2-choices',
11576                 cn:[
11577                     {
11578                         tag: 'li',
11579                         cls: 'roo-select2-search-field',
11580                         cn: [
11581
11582                             inputblock
11583                         ]
11584                     }
11585                 ]
11586             };
11587                 
11588         }
11589         
11590         var combobox = {
11591             cls: 'roo-select2-container input-group',
11592             cn: [
11593                  {
11594                     tag: 'input',
11595                     type : 'hidden',
11596                     cls: 'form-hidden-field'
11597                 },
11598                 ibwrap
11599             ]
11600         };
11601         
11602         if(!this.multiple && this.showToggleBtn){
11603             
11604             var caret = {
11605                         tag: 'span',
11606                         cls: 'caret'
11607              };
11608             if (this.caret != false) {
11609                 caret = {
11610                      tag: 'i',
11611                      cls: 'fa fa-' + this.caret
11612                 };
11613                 
11614             }
11615             
11616             combobox.cn.push({
11617                 tag :'span',
11618                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11619                 cn : [
11620                     Roo.bootstrap.version == 3 ? caret : '',
11621                     {
11622                         tag: 'span',
11623                         cls: 'combobox-clear',
11624                         cn  : [
11625                             {
11626                                 tag : 'i',
11627                                 cls: 'icon-remove'
11628                             }
11629                         ]
11630                     }
11631                 ]
11632
11633             })
11634         }
11635         
11636         if(this.multiple){
11637             combobox.cls += ' roo-select2-container-multi';
11638         }
11639          var indicator = {
11640             tag : 'i',
11641             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11642             tooltip : 'This field is required'
11643         };
11644         if (Roo.bootstrap.version == 4) {
11645             indicator = {
11646                 tag : 'i',
11647                 style : 'display:none'
11648             };
11649         }
11650         
11651         
11652         if (align ==='left' && this.fieldLabel.length) {
11653             
11654             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11655
11656             cfg.cn = [
11657                 indicator,
11658                 {
11659                     tag: 'label',
11660                     'for' :  id,
11661                     cls : 'control-label',
11662                     html : this.fieldLabel
11663
11664                 },
11665                 {
11666                     cls : "", 
11667                     cn: [
11668                         combobox
11669                     ]
11670                 }
11671
11672             ];
11673             
11674             var labelCfg = cfg.cn[1];
11675             var contentCfg = cfg.cn[2];
11676             
11677             if(this.indicatorpos == 'right'){
11678                 cfg.cn = [
11679                     {
11680                         tag: 'label',
11681                         'for' :  id,
11682                         cls : 'control-label',
11683                         cn : [
11684                             {
11685                                 tag : 'span',
11686                                 html : this.fieldLabel
11687                             },
11688                             indicator
11689                         ]
11690                     },
11691                     {
11692                         cls : "", 
11693                         cn: [
11694                             combobox
11695                         ]
11696                     }
11697
11698                 ];
11699                 
11700                 labelCfg = cfg.cn[0];
11701                 contentCfg = cfg.cn[1];
11702             }
11703             
11704             if(this.labelWidth > 12){
11705                 labelCfg.style = "width: " + this.labelWidth + 'px';
11706             }
11707             
11708             if(this.labelWidth < 13 && this.labelmd == 0){
11709                 this.labelmd = this.labelWidth;
11710             }
11711             
11712             if(this.labellg > 0){
11713                 labelCfg.cls += ' col-lg-' + this.labellg;
11714                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11715             }
11716             
11717             if(this.labelmd > 0){
11718                 labelCfg.cls += ' col-md-' + this.labelmd;
11719                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11720             }
11721             
11722             if(this.labelsm > 0){
11723                 labelCfg.cls += ' col-sm-' + this.labelsm;
11724                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11725             }
11726             
11727             if(this.labelxs > 0){
11728                 labelCfg.cls += ' col-xs-' + this.labelxs;
11729                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11730             }
11731             
11732         } else if ( this.fieldLabel.length) {
11733 //                Roo.log(" label");
11734             cfg.cn = [
11735                 indicator,
11736                {
11737                    tag: 'label',
11738                    //cls : 'input-group-addon',
11739                    html : this.fieldLabel
11740
11741                },
11742
11743                combobox
11744
11745             ];
11746             
11747             if(this.indicatorpos == 'right'){
11748                 
11749                 cfg.cn = [
11750                     {
11751                        tag: 'label',
11752                        cn : [
11753                            {
11754                                tag : 'span',
11755                                html : this.fieldLabel
11756                            },
11757                            indicator
11758                        ]
11759
11760                     },
11761                     combobox
11762
11763                 ];
11764
11765             }
11766
11767         } else {
11768             
11769 //                Roo.log(" no label && no align");
11770                 cfg = combobox
11771                      
11772                 
11773         }
11774         
11775         var settings=this;
11776         ['xs','sm','md','lg'].map(function(size){
11777             if (settings[size]) {
11778                 cfg.cls += ' col-' + size + '-' + settings[size];
11779             }
11780         });
11781         
11782         return cfg;
11783         
11784     },
11785     
11786     
11787     
11788     // private
11789     onResize : function(w, h){
11790 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11791 //        if(typeof w == 'number'){
11792 //            var x = w - this.trigger.getWidth();
11793 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11794 //            this.trigger.setStyle('left', x+'px');
11795 //        }
11796     },
11797
11798     // private
11799     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11800
11801     // private
11802     getResizeEl : function(){
11803         return this.inputEl();
11804     },
11805
11806     // private
11807     getPositionEl : function(){
11808         return this.inputEl();
11809     },
11810
11811     // private
11812     alignErrorIcon : function(){
11813         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11814     },
11815
11816     // private
11817     initEvents : function(){
11818         
11819         this.createList();
11820         
11821         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11822         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11823         if(!this.multiple && this.showToggleBtn){
11824             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11825             if(this.hideTrigger){
11826                 this.trigger.setDisplayed(false);
11827             }
11828             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11829         }
11830         
11831         if(this.multiple){
11832             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11833         }
11834         
11835         if(this.removable && !this.editable && !this.tickable){
11836             var close = this.closeTriggerEl();
11837             
11838             if(close){
11839                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11840                 close.on('click', this.removeBtnClick, this, close);
11841             }
11842         }
11843         
11844         //this.trigger.addClassOnOver('x-form-trigger-over');
11845         //this.trigger.addClassOnClick('x-form-trigger-click');
11846         
11847         //if(!this.width){
11848         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11849         //}
11850     },
11851     
11852     closeTriggerEl : function()
11853     {
11854         var close = this.el.select('.roo-combo-removable-btn', true).first();
11855         return close ? close : false;
11856     },
11857     
11858     removeBtnClick : function(e, h, el)
11859     {
11860         e.preventDefault();
11861         
11862         if(this.fireEvent("remove", this) !== false){
11863             this.reset();
11864             this.fireEvent("afterremove", this)
11865         }
11866     },
11867     
11868     createList : function()
11869     {
11870         this.list = Roo.get(document.body).createChild({
11871             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11872             cls: 'typeahead typeahead-long dropdown-menu',
11873             style: 'display:none'
11874         });
11875         
11876         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11877         
11878     },
11879
11880     // private
11881     initTrigger : function(){
11882        
11883     },
11884
11885     // private
11886     onDestroy : function(){
11887         if(this.trigger){
11888             this.trigger.removeAllListeners();
11889           //  this.trigger.remove();
11890         }
11891         //if(this.wrap){
11892         //    this.wrap.remove();
11893         //}
11894         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11895     },
11896
11897     // private
11898     onFocus : function(){
11899         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11900         /*
11901         if(!this.mimicing){
11902             this.wrap.addClass('x-trigger-wrap-focus');
11903             this.mimicing = true;
11904             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11905             if(this.monitorTab){
11906                 this.el.on("keydown", this.checkTab, this);
11907             }
11908         }
11909         */
11910     },
11911
11912     // private
11913     checkTab : function(e){
11914         if(e.getKey() == e.TAB){
11915             this.triggerBlur();
11916         }
11917     },
11918
11919     // private
11920     onBlur : function(){
11921         // do nothing
11922     },
11923
11924     // private
11925     mimicBlur : function(e, t){
11926         /*
11927         if(!this.wrap.contains(t) && this.validateBlur()){
11928             this.triggerBlur();
11929         }
11930         */
11931     },
11932
11933     // private
11934     triggerBlur : function(){
11935         this.mimicing = false;
11936         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11937         if(this.monitorTab){
11938             this.el.un("keydown", this.checkTab, this);
11939         }
11940         //this.wrap.removeClass('x-trigger-wrap-focus');
11941         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11942     },
11943
11944     // private
11945     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11946     validateBlur : function(e, t){
11947         return true;
11948     },
11949
11950     // private
11951     onDisable : function(){
11952         this.inputEl().dom.disabled = true;
11953         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11954         //if(this.wrap){
11955         //    this.wrap.addClass('x-item-disabled');
11956         //}
11957     },
11958
11959     // private
11960     onEnable : function(){
11961         this.inputEl().dom.disabled = false;
11962         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11963         //if(this.wrap){
11964         //    this.el.removeClass('x-item-disabled');
11965         //}
11966     },
11967
11968     // private
11969     onShow : function(){
11970         var ae = this.getActionEl();
11971         
11972         if(ae){
11973             ae.dom.style.display = '';
11974             ae.dom.style.visibility = 'visible';
11975         }
11976     },
11977
11978     // private
11979     
11980     onHide : function(){
11981         var ae = this.getActionEl();
11982         ae.dom.style.display = 'none';
11983     },
11984
11985     /**
11986      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11987      * by an implementing function.
11988      * @method
11989      * @param {EventObject} e
11990      */
11991     onTriggerClick : Roo.emptyFn
11992 });
11993  /*
11994  * Based on:
11995  * Ext JS Library 1.1.1
11996  * Copyright(c) 2006-2007, Ext JS, LLC.
11997  *
11998  * Originally Released Under LGPL - original licence link has changed is not relivant.
11999  *
12000  * Fork - LGPL
12001  * <script type="text/javascript">
12002  */
12003
12004
12005 /**
12006  * @class Roo.data.SortTypes
12007  * @singleton
12008  * Defines the default sorting (casting?) comparison functions used when sorting data.
12009  */
12010 Roo.data.SortTypes = {
12011     /**
12012      * Default sort that does nothing
12013      * @param {Mixed} s The value being converted
12014      * @return {Mixed} The comparison value
12015      */
12016     none : function(s){
12017         return s;
12018     },
12019     
12020     /**
12021      * The regular expression used to strip tags
12022      * @type {RegExp}
12023      * @property
12024      */
12025     stripTagsRE : /<\/?[^>]+>/gi,
12026     
12027     /**
12028      * Strips all HTML tags to sort on text only
12029      * @param {Mixed} s The value being converted
12030      * @return {String} The comparison value
12031      */
12032     asText : function(s){
12033         return String(s).replace(this.stripTagsRE, "");
12034     },
12035     
12036     /**
12037      * Strips all HTML tags to sort on text only - Case insensitive
12038      * @param {Mixed} s The value being converted
12039      * @return {String} The comparison value
12040      */
12041     asUCText : function(s){
12042         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12043     },
12044     
12045     /**
12046      * Case insensitive string
12047      * @param {Mixed} s The value being converted
12048      * @return {String} The comparison value
12049      */
12050     asUCString : function(s) {
12051         return String(s).toUpperCase();
12052     },
12053     
12054     /**
12055      * Date sorting
12056      * @param {Mixed} s The value being converted
12057      * @return {Number} The comparison value
12058      */
12059     asDate : function(s) {
12060         if(!s){
12061             return 0;
12062         }
12063         if(s instanceof Date){
12064             return s.getTime();
12065         }
12066         return Date.parse(String(s));
12067     },
12068     
12069     /**
12070      * Float sorting
12071      * @param {Mixed} s The value being converted
12072      * @return {Float} The comparison value
12073      */
12074     asFloat : function(s) {
12075         var val = parseFloat(String(s).replace(/,/g, ""));
12076         if(isNaN(val)) {
12077             val = 0;
12078         }
12079         return val;
12080     },
12081     
12082     /**
12083      * Integer sorting
12084      * @param {Mixed} s The value being converted
12085      * @return {Number} The comparison value
12086      */
12087     asInt : function(s) {
12088         var val = parseInt(String(s).replace(/,/g, ""));
12089         if(isNaN(val)) {
12090             val = 0;
12091         }
12092         return val;
12093     }
12094 };/*
12095  * Based on:
12096  * Ext JS Library 1.1.1
12097  * Copyright(c) 2006-2007, Ext JS, LLC.
12098  *
12099  * Originally Released Under LGPL - original licence link has changed is not relivant.
12100  *
12101  * Fork - LGPL
12102  * <script type="text/javascript">
12103  */
12104
12105 /**
12106 * @class Roo.data.Record
12107  * Instances of this class encapsulate both record <em>definition</em> information, and record
12108  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12109  * to access Records cached in an {@link Roo.data.Store} object.<br>
12110  * <p>
12111  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12112  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12113  * objects.<br>
12114  * <p>
12115  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12116  * @constructor
12117  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12118  * {@link #create}. The parameters are the same.
12119  * @param {Array} data An associative Array of data values keyed by the field name.
12120  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12121  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12122  * not specified an integer id is generated.
12123  */
12124 Roo.data.Record = function(data, id){
12125     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12126     this.data = data;
12127 };
12128
12129 /**
12130  * Generate a constructor for a specific record layout.
12131  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12132  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12133  * Each field definition object may contain the following properties: <ul>
12134  * <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,
12135  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12136  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12137  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12138  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12139  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12140  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12141  * this may be omitted.</p></li>
12142  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12143  * <ul><li>auto (Default, implies no conversion)</li>
12144  * <li>string</li>
12145  * <li>int</li>
12146  * <li>float</li>
12147  * <li>boolean</li>
12148  * <li>date</li></ul></p></li>
12149  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12150  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12151  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12152  * by the Reader into an object that will be stored in the Record. It is passed the
12153  * following parameters:<ul>
12154  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12155  * </ul></p></li>
12156  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12157  * </ul>
12158  * <br>usage:<br><pre><code>
12159 var TopicRecord = Roo.data.Record.create(
12160     {name: 'title', mapping: 'topic_title'},
12161     {name: 'author', mapping: 'username'},
12162     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12163     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12164     {name: 'lastPoster', mapping: 'user2'},
12165     {name: 'excerpt', mapping: 'post_text'}
12166 );
12167
12168 var myNewRecord = new TopicRecord({
12169     title: 'Do my job please',
12170     author: 'noobie',
12171     totalPosts: 1,
12172     lastPost: new Date(),
12173     lastPoster: 'Animal',
12174     excerpt: 'No way dude!'
12175 });
12176 myStore.add(myNewRecord);
12177 </code></pre>
12178  * @method create
12179  * @static
12180  */
12181 Roo.data.Record.create = function(o){
12182     var f = function(){
12183         f.superclass.constructor.apply(this, arguments);
12184     };
12185     Roo.extend(f, Roo.data.Record);
12186     var p = f.prototype;
12187     p.fields = new Roo.util.MixedCollection(false, function(field){
12188         return field.name;
12189     });
12190     for(var i = 0, len = o.length; i < len; i++){
12191         p.fields.add(new Roo.data.Field(o[i]));
12192     }
12193     f.getField = function(name){
12194         return p.fields.get(name);  
12195     };
12196     return f;
12197 };
12198
12199 Roo.data.Record.AUTO_ID = 1000;
12200 Roo.data.Record.EDIT = 'edit';
12201 Roo.data.Record.REJECT = 'reject';
12202 Roo.data.Record.COMMIT = 'commit';
12203
12204 Roo.data.Record.prototype = {
12205     /**
12206      * Readonly flag - true if this record has been modified.
12207      * @type Boolean
12208      */
12209     dirty : false,
12210     editing : false,
12211     error: null,
12212     modified: null,
12213
12214     // private
12215     join : function(store){
12216         this.store = store;
12217     },
12218
12219     /**
12220      * Set the named field to the specified value.
12221      * @param {String} name The name of the field to set.
12222      * @param {Object} value The value to set the field to.
12223      */
12224     set : function(name, value){
12225         if(this.data[name] == value){
12226             return;
12227         }
12228         this.dirty = true;
12229         if(!this.modified){
12230             this.modified = {};
12231         }
12232         if(typeof this.modified[name] == 'undefined'){
12233             this.modified[name] = this.data[name];
12234         }
12235         this.data[name] = value;
12236         if(!this.editing && this.store){
12237             this.store.afterEdit(this);
12238         }       
12239     },
12240
12241     /**
12242      * Get the value of the named field.
12243      * @param {String} name The name of the field to get the value of.
12244      * @return {Object} The value of the field.
12245      */
12246     get : function(name){
12247         return this.data[name]; 
12248     },
12249
12250     // private
12251     beginEdit : function(){
12252         this.editing = true;
12253         this.modified = {}; 
12254     },
12255
12256     // private
12257     cancelEdit : function(){
12258         this.editing = false;
12259         delete this.modified;
12260     },
12261
12262     // private
12263     endEdit : function(){
12264         this.editing = false;
12265         if(this.dirty && this.store){
12266             this.store.afterEdit(this);
12267         }
12268     },
12269
12270     /**
12271      * Usually called by the {@link Roo.data.Store} which owns the Record.
12272      * Rejects all changes made to the Record since either creation, or the last commit operation.
12273      * Modified fields are reverted to their original values.
12274      * <p>
12275      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12276      * of reject operations.
12277      */
12278     reject : function(){
12279         var m = this.modified;
12280         for(var n in m){
12281             if(typeof m[n] != "function"){
12282                 this.data[n] = m[n];
12283             }
12284         }
12285         this.dirty = false;
12286         delete this.modified;
12287         this.editing = false;
12288         if(this.store){
12289             this.store.afterReject(this);
12290         }
12291     },
12292
12293     /**
12294      * Usually called by the {@link Roo.data.Store} which owns the Record.
12295      * Commits all changes made to the Record since either creation, or the last commit operation.
12296      * <p>
12297      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12298      * of commit operations.
12299      */
12300     commit : function(){
12301         this.dirty = false;
12302         delete this.modified;
12303         this.editing = false;
12304         if(this.store){
12305             this.store.afterCommit(this);
12306         }
12307     },
12308
12309     // private
12310     hasError : function(){
12311         return this.error != null;
12312     },
12313
12314     // private
12315     clearError : function(){
12316         this.error = null;
12317     },
12318
12319     /**
12320      * Creates a copy of this record.
12321      * @param {String} id (optional) A new record id if you don't want to use this record's id
12322      * @return {Record}
12323      */
12324     copy : function(newId) {
12325         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12326     }
12327 };/*
12328  * Based on:
12329  * Ext JS Library 1.1.1
12330  * Copyright(c) 2006-2007, Ext JS, LLC.
12331  *
12332  * Originally Released Under LGPL - original licence link has changed is not relivant.
12333  *
12334  * Fork - LGPL
12335  * <script type="text/javascript">
12336  */
12337
12338
12339
12340 /**
12341  * @class Roo.data.Store
12342  * @extends Roo.util.Observable
12343  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12344  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12345  * <p>
12346  * 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
12347  * has no knowledge of the format of the data returned by the Proxy.<br>
12348  * <p>
12349  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12350  * instances from the data object. These records are cached and made available through accessor functions.
12351  * @constructor
12352  * Creates a new Store.
12353  * @param {Object} config A config object containing the objects needed for the Store to access data,
12354  * and read the data into Records.
12355  */
12356 Roo.data.Store = function(config){
12357     this.data = new Roo.util.MixedCollection(false);
12358     this.data.getKey = function(o){
12359         return o.id;
12360     };
12361     this.baseParams = {};
12362     // private
12363     this.paramNames = {
12364         "start" : "start",
12365         "limit" : "limit",
12366         "sort" : "sort",
12367         "dir" : "dir",
12368         "multisort" : "_multisort"
12369     };
12370
12371     if(config && config.data){
12372         this.inlineData = config.data;
12373         delete config.data;
12374     }
12375
12376     Roo.apply(this, config);
12377     
12378     if(this.reader){ // reader passed
12379         this.reader = Roo.factory(this.reader, Roo.data);
12380         this.reader.xmodule = this.xmodule || false;
12381         if(!this.recordType){
12382             this.recordType = this.reader.recordType;
12383         }
12384         if(this.reader.onMetaChange){
12385             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12386         }
12387     }
12388
12389     if(this.recordType){
12390         this.fields = this.recordType.prototype.fields;
12391     }
12392     this.modified = [];
12393
12394     this.addEvents({
12395         /**
12396          * @event datachanged
12397          * Fires when the data cache has changed, and a widget which is using this Store
12398          * as a Record cache should refresh its view.
12399          * @param {Store} this
12400          */
12401         datachanged : true,
12402         /**
12403          * @event metachange
12404          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12405          * @param {Store} this
12406          * @param {Object} meta The JSON metadata
12407          */
12408         metachange : true,
12409         /**
12410          * @event add
12411          * Fires when Records have been added to the Store
12412          * @param {Store} this
12413          * @param {Roo.data.Record[]} records The array of Records added
12414          * @param {Number} index The index at which the record(s) were added
12415          */
12416         add : true,
12417         /**
12418          * @event remove
12419          * Fires when a Record has been removed from the Store
12420          * @param {Store} this
12421          * @param {Roo.data.Record} record The Record that was removed
12422          * @param {Number} index The index at which the record was removed
12423          */
12424         remove : true,
12425         /**
12426          * @event update
12427          * Fires when a Record has been updated
12428          * @param {Store} this
12429          * @param {Roo.data.Record} record The Record that was updated
12430          * @param {String} operation The update operation being performed.  Value may be one of:
12431          * <pre><code>
12432  Roo.data.Record.EDIT
12433  Roo.data.Record.REJECT
12434  Roo.data.Record.COMMIT
12435          * </code></pre>
12436          */
12437         update : true,
12438         /**
12439          * @event clear
12440          * Fires when the data cache has been cleared.
12441          * @param {Store} this
12442          */
12443         clear : true,
12444         /**
12445          * @event beforeload
12446          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12447          * the load action will be canceled.
12448          * @param {Store} this
12449          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12450          */
12451         beforeload : true,
12452         /**
12453          * @event beforeloadadd
12454          * Fires after a new set of Records has been loaded.
12455          * @param {Store} this
12456          * @param {Roo.data.Record[]} records The Records that were loaded
12457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12458          */
12459         beforeloadadd : true,
12460         /**
12461          * @event load
12462          * Fires after a new set of Records has been loaded, before they are added to the store.
12463          * @param {Store} this
12464          * @param {Roo.data.Record[]} records The Records that were loaded
12465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12466          * @params {Object} return from reader
12467          */
12468         load : true,
12469         /**
12470          * @event loadexception
12471          * Fires if an exception occurs in the Proxy during loading.
12472          * Called with the signature of the Proxy's "loadexception" event.
12473          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12474          * 
12475          * @param {Proxy} 
12476          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12477          * @param {Object} load options 
12478          * @param {Object} jsonData from your request (normally this contains the Exception)
12479          */
12480         loadexception : true
12481     });
12482     
12483     if(this.proxy){
12484         this.proxy = Roo.factory(this.proxy, Roo.data);
12485         this.proxy.xmodule = this.xmodule || false;
12486         this.relayEvents(this.proxy,  ["loadexception"]);
12487     }
12488     this.sortToggle = {};
12489     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12490
12491     Roo.data.Store.superclass.constructor.call(this);
12492
12493     if(this.inlineData){
12494         this.loadData(this.inlineData);
12495         delete this.inlineData;
12496     }
12497 };
12498
12499 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12500      /**
12501     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12502     * without a remote query - used by combo/forms at present.
12503     */
12504     
12505     /**
12506     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12507     */
12508     /**
12509     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12510     */
12511     /**
12512     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12513     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12514     */
12515     /**
12516     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12517     * on any HTTP request
12518     */
12519     /**
12520     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12521     */
12522     /**
12523     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12524     */
12525     multiSort: false,
12526     /**
12527     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12528     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12529     */
12530     remoteSort : false,
12531
12532     /**
12533     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12534      * loaded or when a record is removed. (defaults to false).
12535     */
12536     pruneModifiedRecords : false,
12537
12538     // private
12539     lastOptions : null,
12540
12541     /**
12542      * Add Records to the Store and fires the add event.
12543      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12544      */
12545     add : function(records){
12546         records = [].concat(records);
12547         for(var i = 0, len = records.length; i < len; i++){
12548             records[i].join(this);
12549         }
12550         var index = this.data.length;
12551         this.data.addAll(records);
12552         this.fireEvent("add", this, records, index);
12553     },
12554
12555     /**
12556      * Remove a Record from the Store and fires the remove event.
12557      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12558      */
12559     remove : function(record){
12560         var index = this.data.indexOf(record);
12561         this.data.removeAt(index);
12562  
12563         if(this.pruneModifiedRecords){
12564             this.modified.remove(record);
12565         }
12566         this.fireEvent("remove", this, record, index);
12567     },
12568
12569     /**
12570      * Remove all Records from the Store and fires the clear event.
12571      */
12572     removeAll : function(){
12573         this.data.clear();
12574         if(this.pruneModifiedRecords){
12575             this.modified = [];
12576         }
12577         this.fireEvent("clear", this);
12578     },
12579
12580     /**
12581      * Inserts Records to the Store at the given index and fires the add event.
12582      * @param {Number} index The start index at which to insert the passed Records.
12583      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12584      */
12585     insert : function(index, records){
12586         records = [].concat(records);
12587         for(var i = 0, len = records.length; i < len; i++){
12588             this.data.insert(index, records[i]);
12589             records[i].join(this);
12590         }
12591         this.fireEvent("add", this, records, index);
12592     },
12593
12594     /**
12595      * Get the index within the cache of the passed Record.
12596      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12597      * @return {Number} The index of the passed Record. Returns -1 if not found.
12598      */
12599     indexOf : function(record){
12600         return this.data.indexOf(record);
12601     },
12602
12603     /**
12604      * Get the index within the cache of the Record with the passed id.
12605      * @param {String} id The id of the Record to find.
12606      * @return {Number} The index of the Record. Returns -1 if not found.
12607      */
12608     indexOfId : function(id){
12609         return this.data.indexOfKey(id);
12610     },
12611
12612     /**
12613      * Get the Record with the specified id.
12614      * @param {String} id The id of the Record to find.
12615      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12616      */
12617     getById : function(id){
12618         return this.data.key(id);
12619     },
12620
12621     /**
12622      * Get the Record at the specified index.
12623      * @param {Number} index The index of the Record to find.
12624      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12625      */
12626     getAt : function(index){
12627         return this.data.itemAt(index);
12628     },
12629
12630     /**
12631      * Returns a range of Records between specified indices.
12632      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12633      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12634      * @return {Roo.data.Record[]} An array of Records
12635      */
12636     getRange : function(start, end){
12637         return this.data.getRange(start, end);
12638     },
12639
12640     // private
12641     storeOptions : function(o){
12642         o = Roo.apply({}, o);
12643         delete o.callback;
12644         delete o.scope;
12645         this.lastOptions = o;
12646     },
12647
12648     /**
12649      * Loads the Record cache from the configured Proxy using the configured Reader.
12650      * <p>
12651      * If using remote paging, then the first load call must specify the <em>start</em>
12652      * and <em>limit</em> properties in the options.params property to establish the initial
12653      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12654      * <p>
12655      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12656      * and this call will return before the new data has been loaded. Perform any post-processing
12657      * in a callback function, or in a "load" event handler.</strong>
12658      * <p>
12659      * @param {Object} options An object containing properties which control loading options:<ul>
12660      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12661      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12662      * passed the following arguments:<ul>
12663      * <li>r : Roo.data.Record[]</li>
12664      * <li>options: Options object from the load call</li>
12665      * <li>success: Boolean success indicator</li></ul></li>
12666      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12667      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12668      * </ul>
12669      */
12670     load : function(options){
12671         options = options || {};
12672         if(this.fireEvent("beforeload", this, options) !== false){
12673             this.storeOptions(options);
12674             var p = Roo.apply(options.params || {}, this.baseParams);
12675             // if meta was not loaded from remote source.. try requesting it.
12676             if (!this.reader.metaFromRemote) {
12677                 p._requestMeta = 1;
12678             }
12679             if(this.sortInfo && this.remoteSort){
12680                 var pn = this.paramNames;
12681                 p[pn["sort"]] = this.sortInfo.field;
12682                 p[pn["dir"]] = this.sortInfo.direction;
12683             }
12684             if (this.multiSort) {
12685                 var pn = this.paramNames;
12686                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12687             }
12688             
12689             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12690         }
12691     },
12692
12693     /**
12694      * Reloads the Record cache from the configured Proxy using the configured Reader and
12695      * the options from the last load operation performed.
12696      * @param {Object} options (optional) An object containing properties which may override the options
12697      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12698      * the most recently used options are reused).
12699      */
12700     reload : function(options){
12701         this.load(Roo.applyIf(options||{}, this.lastOptions));
12702     },
12703
12704     // private
12705     // Called as a callback by the Reader during a load operation.
12706     loadRecords : function(o, options, success){
12707         if(!o || success === false){
12708             if(success !== false){
12709                 this.fireEvent("load", this, [], options, o);
12710             }
12711             if(options.callback){
12712                 options.callback.call(options.scope || this, [], options, false);
12713             }
12714             return;
12715         }
12716         // if data returned failure - throw an exception.
12717         if (o.success === false) {
12718             // show a message if no listener is registered.
12719             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12720                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12721             }
12722             // loadmask wil be hooked into this..
12723             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12724             return;
12725         }
12726         var r = o.records, t = o.totalRecords || r.length;
12727         
12728         this.fireEvent("beforeloadadd", this, r, options, o);
12729         
12730         if(!options || options.add !== true){
12731             if(this.pruneModifiedRecords){
12732                 this.modified = [];
12733             }
12734             for(var i = 0, len = r.length; i < len; i++){
12735                 r[i].join(this);
12736             }
12737             if(this.snapshot){
12738                 this.data = this.snapshot;
12739                 delete this.snapshot;
12740             }
12741             this.data.clear();
12742             this.data.addAll(r);
12743             this.totalLength = t;
12744             this.applySort();
12745             this.fireEvent("datachanged", this);
12746         }else{
12747             this.totalLength = Math.max(t, this.data.length+r.length);
12748             this.add(r);
12749         }
12750         
12751         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12752                 
12753             var e = new Roo.data.Record({});
12754
12755             e.set(this.parent.displayField, this.parent.emptyTitle);
12756             e.set(this.parent.valueField, '');
12757
12758             this.insert(0, e);
12759         }
12760             
12761         this.fireEvent("load", this, r, options, o);
12762         if(options.callback){
12763             options.callback.call(options.scope || this, r, options, true);
12764         }
12765     },
12766
12767
12768     /**
12769      * Loads data from a passed data block. A Reader which understands the format of the data
12770      * must have been configured in the constructor.
12771      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12772      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12773      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12774      */
12775     loadData : function(o, append){
12776         var r = this.reader.readRecords(o);
12777         this.loadRecords(r, {add: append}, true);
12778     },
12779     
12780      /**
12781      * using 'cn' the nested child reader read the child array into it's child stores.
12782      * @param {Object} rec The record with a 'children array
12783      */
12784     loadDataFromChildren : function(rec)
12785     {
12786         this.loadData(this.reader.toLoadData(rec));
12787     },
12788     
12789
12790     /**
12791      * Gets the number of cached records.
12792      * <p>
12793      * <em>If using paging, this may not be the total size of the dataset. If the data object
12794      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12795      * the data set size</em>
12796      */
12797     getCount : function(){
12798         return this.data.length || 0;
12799     },
12800
12801     /**
12802      * Gets the total number of records in the dataset as returned by the server.
12803      * <p>
12804      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12805      * the dataset size</em>
12806      */
12807     getTotalCount : function(){
12808         return this.totalLength || 0;
12809     },
12810
12811     /**
12812      * Returns the sort state of the Store as an object with two properties:
12813      * <pre><code>
12814  field {String} The name of the field by which the Records are sorted
12815  direction {String} The sort order, "ASC" or "DESC"
12816      * </code></pre>
12817      */
12818     getSortState : function(){
12819         return this.sortInfo;
12820     },
12821
12822     // private
12823     applySort : function(){
12824         if(this.sortInfo && !this.remoteSort){
12825             var s = this.sortInfo, f = s.field;
12826             var st = this.fields.get(f).sortType;
12827             var fn = function(r1, r2){
12828                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12829                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12830             };
12831             this.data.sort(s.direction, fn);
12832             if(this.snapshot && this.snapshot != this.data){
12833                 this.snapshot.sort(s.direction, fn);
12834             }
12835         }
12836     },
12837
12838     /**
12839      * Sets the default sort column and order to be used by the next load operation.
12840      * @param {String} fieldName The name of the field to sort by.
12841      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12842      */
12843     setDefaultSort : function(field, dir){
12844         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12845     },
12846
12847     /**
12848      * Sort the Records.
12849      * If remote sorting is used, the sort is performed on the server, and the cache is
12850      * reloaded. If local sorting is used, the cache is sorted internally.
12851      * @param {String} fieldName The name of the field to sort by.
12852      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12853      */
12854     sort : function(fieldName, dir){
12855         var f = this.fields.get(fieldName);
12856         if(!dir){
12857             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12858             
12859             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12860                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12861             }else{
12862                 dir = f.sortDir;
12863             }
12864         }
12865         this.sortToggle[f.name] = dir;
12866         this.sortInfo = {field: f.name, direction: dir};
12867         if(!this.remoteSort){
12868             this.applySort();
12869             this.fireEvent("datachanged", this);
12870         }else{
12871             this.load(this.lastOptions);
12872         }
12873     },
12874
12875     /**
12876      * Calls the specified function for each of the Records in the cache.
12877      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12878      * Returning <em>false</em> aborts and exits the iteration.
12879      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12880      */
12881     each : function(fn, scope){
12882         this.data.each(fn, scope);
12883     },
12884
12885     /**
12886      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12887      * (e.g., during paging).
12888      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12889      */
12890     getModifiedRecords : function(){
12891         return this.modified;
12892     },
12893
12894     // private
12895     createFilterFn : function(property, value, anyMatch){
12896         if(!value.exec){ // not a regex
12897             value = String(value);
12898             if(value.length == 0){
12899                 return false;
12900             }
12901             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12902         }
12903         return function(r){
12904             return value.test(r.data[property]);
12905         };
12906     },
12907
12908     /**
12909      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12910      * @param {String} property A field on your records
12911      * @param {Number} start The record index to start at (defaults to 0)
12912      * @param {Number} end The last record index to include (defaults to length - 1)
12913      * @return {Number} The sum
12914      */
12915     sum : function(property, start, end){
12916         var rs = this.data.items, v = 0;
12917         start = start || 0;
12918         end = (end || end === 0) ? end : rs.length-1;
12919
12920         for(var i = start; i <= end; i++){
12921             v += (rs[i].data[property] || 0);
12922         }
12923         return v;
12924     },
12925
12926     /**
12927      * Filter the records by a specified property.
12928      * @param {String} field A field on your records
12929      * @param {String/RegExp} value Either a string that the field
12930      * should start with or a RegExp to test against the field
12931      * @param {Boolean} anyMatch True to match any part not just the beginning
12932      */
12933     filter : function(property, value, anyMatch){
12934         var fn = this.createFilterFn(property, value, anyMatch);
12935         return fn ? this.filterBy(fn) : this.clearFilter();
12936     },
12937
12938     /**
12939      * Filter by a function. The specified function will be called with each
12940      * record in this data source. If the function returns true the record is included,
12941      * otherwise it is filtered.
12942      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12943      * @param {Object} scope (optional) The scope of the function (defaults to this)
12944      */
12945     filterBy : function(fn, scope){
12946         this.snapshot = this.snapshot || this.data;
12947         this.data = this.queryBy(fn, scope||this);
12948         this.fireEvent("datachanged", this);
12949     },
12950
12951     /**
12952      * Query the records by a specified property.
12953      * @param {String} field A field on your records
12954      * @param {String/RegExp} value Either a string that the field
12955      * should start with or a RegExp to test against the field
12956      * @param {Boolean} anyMatch True to match any part not just the beginning
12957      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12958      */
12959     query : function(property, value, anyMatch){
12960         var fn = this.createFilterFn(property, value, anyMatch);
12961         return fn ? this.queryBy(fn) : this.data.clone();
12962     },
12963
12964     /**
12965      * Query by a function. The specified function will be called with each
12966      * record in this data source. If the function returns true the record is included
12967      * in the results.
12968      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12969      * @param {Object} scope (optional) The scope of the function (defaults to this)
12970       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12971      **/
12972     queryBy : function(fn, scope){
12973         var data = this.snapshot || this.data;
12974         return data.filterBy(fn, scope||this);
12975     },
12976
12977     /**
12978      * Collects unique values for a particular dataIndex from this store.
12979      * @param {String} dataIndex The property to collect
12980      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12981      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12982      * @return {Array} An array of the unique values
12983      **/
12984     collect : function(dataIndex, allowNull, bypassFilter){
12985         var d = (bypassFilter === true && this.snapshot) ?
12986                 this.snapshot.items : this.data.items;
12987         var v, sv, r = [], l = {};
12988         for(var i = 0, len = d.length; i < len; i++){
12989             v = d[i].data[dataIndex];
12990             sv = String(v);
12991             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12992                 l[sv] = true;
12993                 r[r.length] = v;
12994             }
12995         }
12996         return r;
12997     },
12998
12999     /**
13000      * Revert to a view of the Record cache with no filtering applied.
13001      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13002      */
13003     clearFilter : function(suppressEvent){
13004         if(this.snapshot && this.snapshot != this.data){
13005             this.data = this.snapshot;
13006             delete this.snapshot;
13007             if(suppressEvent !== true){
13008                 this.fireEvent("datachanged", this);
13009             }
13010         }
13011     },
13012
13013     // private
13014     afterEdit : function(record){
13015         if(this.modified.indexOf(record) == -1){
13016             this.modified.push(record);
13017         }
13018         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13019     },
13020     
13021     // private
13022     afterReject : function(record){
13023         this.modified.remove(record);
13024         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13025     },
13026
13027     // private
13028     afterCommit : function(record){
13029         this.modified.remove(record);
13030         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13031     },
13032
13033     /**
13034      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13035      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13036      */
13037     commitChanges : function(){
13038         var m = this.modified.slice(0);
13039         this.modified = [];
13040         for(var i = 0, len = m.length; i < len; i++){
13041             m[i].commit();
13042         }
13043     },
13044
13045     /**
13046      * Cancel outstanding changes on all changed records.
13047      */
13048     rejectChanges : function(){
13049         var m = this.modified.slice(0);
13050         this.modified = [];
13051         for(var i = 0, len = m.length; i < len; i++){
13052             m[i].reject();
13053         }
13054     },
13055
13056     onMetaChange : function(meta, rtype, o){
13057         this.recordType = rtype;
13058         this.fields = rtype.prototype.fields;
13059         delete this.snapshot;
13060         this.sortInfo = meta.sortInfo || this.sortInfo;
13061         this.modified = [];
13062         this.fireEvent('metachange', this, this.reader.meta);
13063     },
13064     
13065     moveIndex : function(data, type)
13066     {
13067         var index = this.indexOf(data);
13068         
13069         var newIndex = index + type;
13070         
13071         this.remove(data);
13072         
13073         this.insert(newIndex, data);
13074         
13075     }
13076 });/*
13077  * Based on:
13078  * Ext JS Library 1.1.1
13079  * Copyright(c) 2006-2007, Ext JS, LLC.
13080  *
13081  * Originally Released Under LGPL - original licence link has changed is not relivant.
13082  *
13083  * Fork - LGPL
13084  * <script type="text/javascript">
13085  */
13086
13087 /**
13088  * @class Roo.data.SimpleStore
13089  * @extends Roo.data.Store
13090  * Small helper class to make creating Stores from Array data easier.
13091  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13092  * @cfg {Array} fields An array of field definition objects, or field name strings.
13093  * @cfg {Object} an existing reader (eg. copied from another store)
13094  * @cfg {Array} data The multi-dimensional array of data
13095  * @constructor
13096  * @param {Object} config
13097  */
13098 Roo.data.SimpleStore = function(config)
13099 {
13100     Roo.data.SimpleStore.superclass.constructor.call(this, {
13101         isLocal : true,
13102         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13103                 id: config.id
13104             },
13105             Roo.data.Record.create(config.fields)
13106         ),
13107         proxy : new Roo.data.MemoryProxy(config.data)
13108     });
13109     this.load();
13110 };
13111 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13112  * Based on:
13113  * Ext JS Library 1.1.1
13114  * Copyright(c) 2006-2007, Ext JS, LLC.
13115  *
13116  * Originally Released Under LGPL - original licence link has changed is not relivant.
13117  *
13118  * Fork - LGPL
13119  * <script type="text/javascript">
13120  */
13121
13122 /**
13123 /**
13124  * @extends Roo.data.Store
13125  * @class Roo.data.JsonStore
13126  * Small helper class to make creating Stores for JSON data easier. <br/>
13127 <pre><code>
13128 var store = new Roo.data.JsonStore({
13129     url: 'get-images.php',
13130     root: 'images',
13131     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13132 });
13133 </code></pre>
13134  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13135  * JsonReader and HttpProxy (unless inline data is provided).</b>
13136  * @cfg {Array} fields An array of field definition objects, or field name strings.
13137  * @constructor
13138  * @param {Object} config
13139  */
13140 Roo.data.JsonStore = function(c){
13141     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13142         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13143         reader: new Roo.data.JsonReader(c, c.fields)
13144     }));
13145 };
13146 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13147  * Based on:
13148  * Ext JS Library 1.1.1
13149  * Copyright(c) 2006-2007, Ext JS, LLC.
13150  *
13151  * Originally Released Under LGPL - original licence link has changed is not relivant.
13152  *
13153  * Fork - LGPL
13154  * <script type="text/javascript">
13155  */
13156
13157  
13158 Roo.data.Field = function(config){
13159     if(typeof config == "string"){
13160         config = {name: config};
13161     }
13162     Roo.apply(this, config);
13163     
13164     if(!this.type){
13165         this.type = "auto";
13166     }
13167     
13168     var st = Roo.data.SortTypes;
13169     // named sortTypes are supported, here we look them up
13170     if(typeof this.sortType == "string"){
13171         this.sortType = st[this.sortType];
13172     }
13173     
13174     // set default sortType for strings and dates
13175     if(!this.sortType){
13176         switch(this.type){
13177             case "string":
13178                 this.sortType = st.asUCString;
13179                 break;
13180             case "date":
13181                 this.sortType = st.asDate;
13182                 break;
13183             default:
13184                 this.sortType = st.none;
13185         }
13186     }
13187
13188     // define once
13189     var stripRe = /[\$,%]/g;
13190
13191     // prebuilt conversion function for this field, instead of
13192     // switching every time we're reading a value
13193     if(!this.convert){
13194         var cv, dateFormat = this.dateFormat;
13195         switch(this.type){
13196             case "":
13197             case "auto":
13198             case undefined:
13199                 cv = function(v){ return v; };
13200                 break;
13201             case "string":
13202                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13203                 break;
13204             case "int":
13205                 cv = function(v){
13206                     return v !== undefined && v !== null && v !== '' ?
13207                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13208                     };
13209                 break;
13210             case "float":
13211                 cv = function(v){
13212                     return v !== undefined && v !== null && v !== '' ?
13213                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13214                     };
13215                 break;
13216             case "bool":
13217             case "boolean":
13218                 cv = function(v){ return v === true || v === "true" || v == 1; };
13219                 break;
13220             case "date":
13221                 cv = function(v){
13222                     if(!v){
13223                         return '';
13224                     }
13225                     if(v instanceof Date){
13226                         return v;
13227                     }
13228                     if(dateFormat){
13229                         if(dateFormat == "timestamp"){
13230                             return new Date(v*1000);
13231                         }
13232                         return Date.parseDate(v, dateFormat);
13233                     }
13234                     var parsed = Date.parse(v);
13235                     return parsed ? new Date(parsed) : null;
13236                 };
13237              break;
13238             
13239         }
13240         this.convert = cv;
13241     }
13242 };
13243
13244 Roo.data.Field.prototype = {
13245     dateFormat: null,
13246     defaultValue: "",
13247     mapping: null,
13248     sortType : null,
13249     sortDir : "ASC"
13250 };/*
13251  * Based on:
13252  * Ext JS Library 1.1.1
13253  * Copyright(c) 2006-2007, Ext JS, LLC.
13254  *
13255  * Originally Released Under LGPL - original licence link has changed is not relivant.
13256  *
13257  * Fork - LGPL
13258  * <script type="text/javascript">
13259  */
13260  
13261 // Base class for reading structured data from a data source.  This class is intended to be
13262 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13263
13264 /**
13265  * @class Roo.data.DataReader
13266  * Base class for reading structured data from a data source.  This class is intended to be
13267  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13268  */
13269
13270 Roo.data.DataReader = function(meta, recordType){
13271     
13272     this.meta = meta;
13273     
13274     this.recordType = recordType instanceof Array ? 
13275         Roo.data.Record.create(recordType) : recordType;
13276 };
13277
13278 Roo.data.DataReader.prototype = {
13279     
13280     
13281     readerType : 'Data',
13282      /**
13283      * Create an empty record
13284      * @param {Object} data (optional) - overlay some values
13285      * @return {Roo.data.Record} record created.
13286      */
13287     newRow :  function(d) {
13288         var da =  {};
13289         this.recordType.prototype.fields.each(function(c) {
13290             switch( c.type) {
13291                 case 'int' : da[c.name] = 0; break;
13292                 case 'date' : da[c.name] = new Date(); break;
13293                 case 'float' : da[c.name] = 0.0; break;
13294                 case 'boolean' : da[c.name] = false; break;
13295                 default : da[c.name] = ""; break;
13296             }
13297             
13298         });
13299         return new this.recordType(Roo.apply(da, d));
13300     }
13301     
13302     
13303 };/*
13304  * Based on:
13305  * Ext JS Library 1.1.1
13306  * Copyright(c) 2006-2007, Ext JS, LLC.
13307  *
13308  * Originally Released Under LGPL - original licence link has changed is not relivant.
13309  *
13310  * Fork - LGPL
13311  * <script type="text/javascript">
13312  */
13313
13314 /**
13315  * @class Roo.data.DataProxy
13316  * @extends Roo.data.Observable
13317  * This class is an abstract base class for implementations which provide retrieval of
13318  * unformatted data objects.<br>
13319  * <p>
13320  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13321  * (of the appropriate type which knows how to parse the data object) to provide a block of
13322  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13323  * <p>
13324  * Custom implementations must implement the load method as described in
13325  * {@link Roo.data.HttpProxy#load}.
13326  */
13327 Roo.data.DataProxy = function(){
13328     this.addEvents({
13329         /**
13330          * @event beforeload
13331          * Fires before a network request is made to retrieve a data object.
13332          * @param {Object} This DataProxy object.
13333          * @param {Object} params The params parameter to the load function.
13334          */
13335         beforeload : true,
13336         /**
13337          * @event load
13338          * Fires before the load method's callback is called.
13339          * @param {Object} This DataProxy object.
13340          * @param {Object} o The data object.
13341          * @param {Object} arg The callback argument object passed to the load function.
13342          */
13343         load : true,
13344         /**
13345          * @event loadexception
13346          * Fires if an Exception occurs during data retrieval.
13347          * @param {Object} This DataProxy object.
13348          * @param {Object} o The data object.
13349          * @param {Object} arg The callback argument object passed to the load function.
13350          * @param {Object} e The Exception.
13351          */
13352         loadexception : true
13353     });
13354     Roo.data.DataProxy.superclass.constructor.call(this);
13355 };
13356
13357 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13358
13359     /**
13360      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13361      */
13362 /*
13363  * Based on:
13364  * Ext JS Library 1.1.1
13365  * Copyright(c) 2006-2007, Ext JS, LLC.
13366  *
13367  * Originally Released Under LGPL - original licence link has changed is not relivant.
13368  *
13369  * Fork - LGPL
13370  * <script type="text/javascript">
13371  */
13372 /**
13373  * @class Roo.data.MemoryProxy
13374  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13375  * to the Reader when its load method is called.
13376  * @constructor
13377  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13378  */
13379 Roo.data.MemoryProxy = function(data){
13380     if (data.data) {
13381         data = data.data;
13382     }
13383     Roo.data.MemoryProxy.superclass.constructor.call(this);
13384     this.data = data;
13385 };
13386
13387 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13388     
13389     /**
13390      * Load data from the requested source (in this case an in-memory
13391      * data object passed to the constructor), read the data object into
13392      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13393      * process that block using the passed callback.
13394      * @param {Object} params This parameter is not used by the MemoryProxy class.
13395      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13396      * object into a block of Roo.data.Records.
13397      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13398      * The function must be passed <ul>
13399      * <li>The Record block object</li>
13400      * <li>The "arg" argument from the load function</li>
13401      * <li>A boolean success indicator</li>
13402      * </ul>
13403      * @param {Object} scope The scope in which to call the callback
13404      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13405      */
13406     load : function(params, reader, callback, scope, arg){
13407         params = params || {};
13408         var result;
13409         try {
13410             result = reader.readRecords(params.data ? params.data :this.data);
13411         }catch(e){
13412             this.fireEvent("loadexception", this, arg, null, e);
13413             callback.call(scope, null, arg, false);
13414             return;
13415         }
13416         callback.call(scope, result, arg, true);
13417     },
13418     
13419     // private
13420     update : function(params, records){
13421         
13422     }
13423 });/*
13424  * Based on:
13425  * Ext JS Library 1.1.1
13426  * Copyright(c) 2006-2007, Ext JS, LLC.
13427  *
13428  * Originally Released Under LGPL - original licence link has changed is not relivant.
13429  *
13430  * Fork - LGPL
13431  * <script type="text/javascript">
13432  */
13433 /**
13434  * @class Roo.data.HttpProxy
13435  * @extends Roo.data.DataProxy
13436  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13437  * configured to reference a certain URL.<br><br>
13438  * <p>
13439  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13440  * from which the running page was served.<br><br>
13441  * <p>
13442  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13443  * <p>
13444  * Be aware that to enable the browser to parse an XML document, the server must set
13445  * the Content-Type header in the HTTP response to "text/xml".
13446  * @constructor
13447  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13448  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13449  * will be used to make the request.
13450  */
13451 Roo.data.HttpProxy = function(conn){
13452     Roo.data.HttpProxy.superclass.constructor.call(this);
13453     // is conn a conn config or a real conn?
13454     this.conn = conn;
13455     this.useAjax = !conn || !conn.events;
13456   
13457 };
13458
13459 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13460     // thse are take from connection...
13461     
13462     /**
13463      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13464      */
13465     /**
13466      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13467      * extra parameters to each request made by this object. (defaults to undefined)
13468      */
13469     /**
13470      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13471      *  to each request made by this object. (defaults to undefined)
13472      */
13473     /**
13474      * @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)
13475      */
13476     /**
13477      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13478      */
13479      /**
13480      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13481      * @type Boolean
13482      */
13483   
13484
13485     /**
13486      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13487      * @type Boolean
13488      */
13489     /**
13490      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13491      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13492      * a finer-grained basis than the DataProxy events.
13493      */
13494     getConnection : function(){
13495         return this.useAjax ? Roo.Ajax : this.conn;
13496     },
13497
13498     /**
13499      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13500      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13501      * process that block using the passed callback.
13502      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13503      * for the request to the remote server.
13504      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13505      * object into a block of Roo.data.Records.
13506      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13507      * The function must be passed <ul>
13508      * <li>The Record block object</li>
13509      * <li>The "arg" argument from the load function</li>
13510      * <li>A boolean success indicator</li>
13511      * </ul>
13512      * @param {Object} scope The scope in which to call the callback
13513      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13514      */
13515     load : function(params, reader, callback, scope, arg){
13516         if(this.fireEvent("beforeload", this, params) !== false){
13517             var  o = {
13518                 params : params || {},
13519                 request: {
13520                     callback : callback,
13521                     scope : scope,
13522                     arg : arg
13523                 },
13524                 reader: reader,
13525                 callback : this.loadResponse,
13526                 scope: this
13527             };
13528             if(this.useAjax){
13529                 Roo.applyIf(o, this.conn);
13530                 if(this.activeRequest){
13531                     Roo.Ajax.abort(this.activeRequest);
13532                 }
13533                 this.activeRequest = Roo.Ajax.request(o);
13534             }else{
13535                 this.conn.request(o);
13536             }
13537         }else{
13538             callback.call(scope||this, null, arg, false);
13539         }
13540     },
13541
13542     // private
13543     loadResponse : function(o, success, response){
13544         delete this.activeRequest;
13545         if(!success){
13546             this.fireEvent("loadexception", this, o, response);
13547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13548             return;
13549         }
13550         var result;
13551         try {
13552             result = o.reader.read(response);
13553         }catch(e){
13554             this.fireEvent("loadexception", this, o, response, e);
13555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13556             return;
13557         }
13558         
13559         this.fireEvent("load", this, o, o.request.arg);
13560         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13561     },
13562
13563     // private
13564     update : function(dataSet){
13565
13566     },
13567
13568     // private
13569     updateResponse : function(dataSet){
13570
13571     }
13572 });/*
13573  * Based on:
13574  * Ext JS Library 1.1.1
13575  * Copyright(c) 2006-2007, Ext JS, LLC.
13576  *
13577  * Originally Released Under LGPL - original licence link has changed is not relivant.
13578  *
13579  * Fork - LGPL
13580  * <script type="text/javascript">
13581  */
13582
13583 /**
13584  * @class Roo.data.ScriptTagProxy
13585  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13586  * other than the originating domain of the running page.<br><br>
13587  * <p>
13588  * <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
13589  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13590  * <p>
13591  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13592  * source code that is used as the source inside a &lt;script> tag.<br><br>
13593  * <p>
13594  * In order for the browser to process the returned data, the server must wrap the data object
13595  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13596  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13597  * depending on whether the callback name was passed:
13598  * <p>
13599  * <pre><code>
13600 boolean scriptTag = false;
13601 String cb = request.getParameter("callback");
13602 if (cb != null) {
13603     scriptTag = true;
13604     response.setContentType("text/javascript");
13605 } else {
13606     response.setContentType("application/x-json");
13607 }
13608 Writer out = response.getWriter();
13609 if (scriptTag) {
13610     out.write(cb + "(");
13611 }
13612 out.print(dataBlock.toJsonString());
13613 if (scriptTag) {
13614     out.write(");");
13615 }
13616 </pre></code>
13617  *
13618  * @constructor
13619  * @param {Object} config A configuration object.
13620  */
13621 Roo.data.ScriptTagProxy = function(config){
13622     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13623     Roo.apply(this, config);
13624     this.head = document.getElementsByTagName("head")[0];
13625 };
13626
13627 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13628
13629 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13630     /**
13631      * @cfg {String} url The URL from which to request the data object.
13632      */
13633     /**
13634      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13635      */
13636     timeout : 30000,
13637     /**
13638      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13639      * the server the name of the callback function set up by the load call to process the returned data object.
13640      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13641      * javascript output which calls this named function passing the data object as its only parameter.
13642      */
13643     callbackParam : "callback",
13644     /**
13645      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13646      * name to the request.
13647      */
13648     nocache : true,
13649
13650     /**
13651      * Load data from the configured URL, read the data object into
13652      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13653      * process that block using the passed callback.
13654      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13655      * for the request to the remote server.
13656      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13657      * object into a block of Roo.data.Records.
13658      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13659      * The function must be passed <ul>
13660      * <li>The Record block object</li>
13661      * <li>The "arg" argument from the load function</li>
13662      * <li>A boolean success indicator</li>
13663      * </ul>
13664      * @param {Object} scope The scope in which to call the callback
13665      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13666      */
13667     load : function(params, reader, callback, scope, arg){
13668         if(this.fireEvent("beforeload", this, params) !== false){
13669
13670             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13671
13672             var url = this.url;
13673             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13674             if(this.nocache){
13675                 url += "&_dc=" + (new Date().getTime());
13676             }
13677             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13678             var trans = {
13679                 id : transId,
13680                 cb : "stcCallback"+transId,
13681                 scriptId : "stcScript"+transId,
13682                 params : params,
13683                 arg : arg,
13684                 url : url,
13685                 callback : callback,
13686                 scope : scope,
13687                 reader : reader
13688             };
13689             var conn = this;
13690
13691             window[trans.cb] = function(o){
13692                 conn.handleResponse(o, trans);
13693             };
13694
13695             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13696
13697             if(this.autoAbort !== false){
13698                 this.abort();
13699             }
13700
13701             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13702
13703             var script = document.createElement("script");
13704             script.setAttribute("src", url);
13705             script.setAttribute("type", "text/javascript");
13706             script.setAttribute("id", trans.scriptId);
13707             this.head.appendChild(script);
13708
13709             this.trans = trans;
13710         }else{
13711             callback.call(scope||this, null, arg, false);
13712         }
13713     },
13714
13715     // private
13716     isLoading : function(){
13717         return this.trans ? true : false;
13718     },
13719
13720     /**
13721      * Abort the current server request.
13722      */
13723     abort : function(){
13724         if(this.isLoading()){
13725             this.destroyTrans(this.trans);
13726         }
13727     },
13728
13729     // private
13730     destroyTrans : function(trans, isLoaded){
13731         this.head.removeChild(document.getElementById(trans.scriptId));
13732         clearTimeout(trans.timeoutId);
13733         if(isLoaded){
13734             window[trans.cb] = undefined;
13735             try{
13736                 delete window[trans.cb];
13737             }catch(e){}
13738         }else{
13739             // if hasn't been loaded, wait for load to remove it to prevent script error
13740             window[trans.cb] = function(){
13741                 window[trans.cb] = undefined;
13742                 try{
13743                     delete window[trans.cb];
13744                 }catch(e){}
13745             };
13746         }
13747     },
13748
13749     // private
13750     handleResponse : function(o, trans){
13751         this.trans = false;
13752         this.destroyTrans(trans, true);
13753         var result;
13754         try {
13755             result = trans.reader.readRecords(o);
13756         }catch(e){
13757             this.fireEvent("loadexception", this, o, trans.arg, e);
13758             trans.callback.call(trans.scope||window, null, trans.arg, false);
13759             return;
13760         }
13761         this.fireEvent("load", this, o, trans.arg);
13762         trans.callback.call(trans.scope||window, result, trans.arg, true);
13763     },
13764
13765     // private
13766     handleFailure : function(trans){
13767         this.trans = false;
13768         this.destroyTrans(trans, false);
13769         this.fireEvent("loadexception", this, null, trans.arg);
13770         trans.callback.call(trans.scope||window, null, trans.arg, false);
13771     }
13772 });/*
13773  * Based on:
13774  * Ext JS Library 1.1.1
13775  * Copyright(c) 2006-2007, Ext JS, LLC.
13776  *
13777  * Originally Released Under LGPL - original licence link has changed is not relivant.
13778  *
13779  * Fork - LGPL
13780  * <script type="text/javascript">
13781  */
13782
13783 /**
13784  * @class Roo.data.JsonReader
13785  * @extends Roo.data.DataReader
13786  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13787  * based on mappings in a provided Roo.data.Record constructor.
13788  * 
13789  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13790  * in the reply previously. 
13791  * 
13792  * <p>
13793  * Example code:
13794  * <pre><code>
13795 var RecordDef = Roo.data.Record.create([
13796     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13797     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13798 ]);
13799 var myReader = new Roo.data.JsonReader({
13800     totalProperty: "results",    // The property which contains the total dataset size (optional)
13801     root: "rows",                // The property which contains an Array of row objects
13802     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13803 }, RecordDef);
13804 </code></pre>
13805  * <p>
13806  * This would consume a JSON file like this:
13807  * <pre><code>
13808 { 'results': 2, 'rows': [
13809     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13810     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13811 }
13812 </code></pre>
13813  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13814  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13815  * paged from the remote server.
13816  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13817  * @cfg {String} root name of the property which contains the Array of row objects.
13818  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13819  * @cfg {Array} fields Array of field definition objects
13820  * @constructor
13821  * Create a new JsonReader
13822  * @param {Object} meta Metadata configuration options
13823  * @param {Object} recordType Either an Array of field definition objects,
13824  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13825  */
13826 Roo.data.JsonReader = function(meta, recordType){
13827     
13828     meta = meta || {};
13829     // set some defaults:
13830     Roo.applyIf(meta, {
13831         totalProperty: 'total',
13832         successProperty : 'success',
13833         root : 'data',
13834         id : 'id'
13835     });
13836     
13837     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13838 };
13839 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13840     
13841     readerType : 'Json',
13842     
13843     /**
13844      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13845      * Used by Store query builder to append _requestMeta to params.
13846      * 
13847      */
13848     metaFromRemote : false,
13849     /**
13850      * This method is only used by a DataProxy which has retrieved data from a remote server.
13851      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13852      * @return {Object} data A data block which is used by an Roo.data.Store object as
13853      * a cache of Roo.data.Records.
13854      */
13855     read : function(response){
13856         var json = response.responseText;
13857        
13858         var o = /* eval:var:o */ eval("("+json+")");
13859         if(!o) {
13860             throw {message: "JsonReader.read: Json object not found"};
13861         }
13862         
13863         if(o.metaData){
13864             
13865             delete this.ef;
13866             this.metaFromRemote = true;
13867             this.meta = o.metaData;
13868             this.recordType = Roo.data.Record.create(o.metaData.fields);
13869             this.onMetaChange(this.meta, this.recordType, o);
13870         }
13871         return this.readRecords(o);
13872     },
13873
13874     // private function a store will implement
13875     onMetaChange : function(meta, recordType, o){
13876
13877     },
13878
13879     /**
13880          * @ignore
13881          */
13882     simpleAccess: function(obj, subsc) {
13883         return obj[subsc];
13884     },
13885
13886         /**
13887          * @ignore
13888          */
13889     getJsonAccessor: function(){
13890         var re = /[\[\.]/;
13891         return function(expr) {
13892             try {
13893                 return(re.test(expr))
13894                     ? new Function("obj", "return obj." + expr)
13895                     : function(obj){
13896                         return obj[expr];
13897                     };
13898             } catch(e){}
13899             return Roo.emptyFn;
13900         };
13901     }(),
13902
13903     /**
13904      * Create a data block containing Roo.data.Records from an XML document.
13905      * @param {Object} o An object which contains an Array of row objects in the property specified
13906      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13907      * which contains the total size of the dataset.
13908      * @return {Object} data A data block which is used by an Roo.data.Store object as
13909      * a cache of Roo.data.Records.
13910      */
13911     readRecords : function(o){
13912         /**
13913          * After any data loads, the raw JSON data is available for further custom processing.
13914          * @type Object
13915          */
13916         this.o = o;
13917         var s = this.meta, Record = this.recordType,
13918             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13919
13920 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13921         if (!this.ef) {
13922             if(s.totalProperty) {
13923                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13924                 }
13925                 if(s.successProperty) {
13926                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13927                 }
13928                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13929                 if (s.id) {
13930                         var g = this.getJsonAccessor(s.id);
13931                         this.getId = function(rec) {
13932                                 var r = g(rec);  
13933                                 return (r === undefined || r === "") ? null : r;
13934                         };
13935                 } else {
13936                         this.getId = function(){return null;};
13937                 }
13938             this.ef = [];
13939             for(var jj = 0; jj < fl; jj++){
13940                 f = fi[jj];
13941                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13942                 this.ef[jj] = this.getJsonAccessor(map);
13943             }
13944         }
13945
13946         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13947         if(s.totalProperty){
13948             var vt = parseInt(this.getTotal(o), 10);
13949             if(!isNaN(vt)){
13950                 totalRecords = vt;
13951             }
13952         }
13953         if(s.successProperty){
13954             var vs = this.getSuccess(o);
13955             if(vs === false || vs === 'false'){
13956                 success = false;
13957             }
13958         }
13959         var records = [];
13960         for(var i = 0; i < c; i++){
13961                 var n = root[i];
13962             var values = {};
13963             var id = this.getId(n);
13964             for(var j = 0; j < fl; j++){
13965                 f = fi[j];
13966             var v = this.ef[j](n);
13967             if (!f.convert) {
13968                 Roo.log('missing convert for ' + f.name);
13969                 Roo.log(f);
13970                 continue;
13971             }
13972             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13973             }
13974             var record = new Record(values, id);
13975             record.json = n;
13976             records[i] = record;
13977         }
13978         return {
13979             raw : o,
13980             success : success,
13981             records : records,
13982             totalRecords : totalRecords
13983         };
13984     },
13985     // used when loading children.. @see loadDataFromChildren
13986     toLoadData: function(rec)
13987     {
13988         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13989         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13990         return { data : data, total : data.length };
13991         
13992     }
13993 });/*
13994  * Based on:
13995  * Ext JS Library 1.1.1
13996  * Copyright(c) 2006-2007, Ext JS, LLC.
13997  *
13998  * Originally Released Under LGPL - original licence link has changed is not relivant.
13999  *
14000  * Fork - LGPL
14001  * <script type="text/javascript">
14002  */
14003
14004 /**
14005  * @class Roo.data.ArrayReader
14006  * @extends Roo.data.DataReader
14007  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14008  * Each element of that Array represents a row of data fields. The
14009  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14010  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14011  * <p>
14012  * Example code:.
14013  * <pre><code>
14014 var RecordDef = Roo.data.Record.create([
14015     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14016     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14017 ]);
14018 var myReader = new Roo.data.ArrayReader({
14019     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14020 }, RecordDef);
14021 </code></pre>
14022  * <p>
14023  * This would consume an Array like this:
14024  * <pre><code>
14025 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14026   </code></pre>
14027  
14028  * @constructor
14029  * Create a new JsonReader
14030  * @param {Object} meta Metadata configuration options.
14031  * @param {Object|Array} recordType Either an Array of field definition objects
14032  * 
14033  * @cfg {Array} fields Array of field definition objects
14034  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14035  * as specified to {@link Roo.data.Record#create},
14036  * or an {@link Roo.data.Record} object
14037  *
14038  * 
14039  * created using {@link Roo.data.Record#create}.
14040  */
14041 Roo.data.ArrayReader = function(meta, recordType)
14042 {    
14043     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14044 };
14045
14046 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14047     
14048       /**
14049      * Create a data block containing Roo.data.Records from an XML document.
14050      * @param {Object} o An Array of row objects which represents the dataset.
14051      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14052      * a cache of Roo.data.Records.
14053      */
14054     readRecords : function(o)
14055     {
14056         var sid = this.meta ? this.meta.id : null;
14057         var recordType = this.recordType, fields = recordType.prototype.fields;
14058         var records = [];
14059         var root = o;
14060         for(var i = 0; i < root.length; i++){
14061                 var n = root[i];
14062             var values = {};
14063             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14064             for(var j = 0, jlen = fields.length; j < jlen; j++){
14065                 var f = fields.items[j];
14066                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14067                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14068                 v = f.convert(v);
14069                 values[f.name] = v;
14070             }
14071             var record = new recordType(values, id);
14072             record.json = n;
14073             records[records.length] = record;
14074         }
14075         return {
14076             records : records,
14077             totalRecords : records.length
14078         };
14079     },
14080     // used when loading children.. @see loadDataFromChildren
14081     toLoadData: function(rec)
14082     {
14083         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14084         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14085         
14086     }
14087     
14088     
14089 });/*
14090  * - LGPL
14091  * * 
14092  */
14093
14094 /**
14095  * @class Roo.bootstrap.ComboBox
14096  * @extends Roo.bootstrap.TriggerField
14097  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14098  * @cfg {Boolean} append (true|false) default false
14099  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14100  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14101  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14102  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14103  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14104  * @cfg {Boolean} animate default true
14105  * @cfg {Boolean} emptyResultText only for touch device
14106  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14107  * @cfg {String} emptyTitle default ''
14108  * @constructor
14109  * Create a new ComboBox.
14110  * @param {Object} config Configuration options
14111  */
14112 Roo.bootstrap.ComboBox = function(config){
14113     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14114     this.addEvents({
14115         /**
14116          * @event expand
14117          * Fires when the dropdown list is expanded
14118         * @param {Roo.bootstrap.ComboBox} combo This combo box
14119         */
14120         'expand' : true,
14121         /**
14122          * @event collapse
14123          * Fires when the dropdown list is collapsed
14124         * @param {Roo.bootstrap.ComboBox} combo This combo box
14125         */
14126         'collapse' : true,
14127         /**
14128          * @event beforeselect
14129          * Fires before a list item is selected. Return false to cancel the selection.
14130         * @param {Roo.bootstrap.ComboBox} combo This combo box
14131         * @param {Roo.data.Record} record The data record returned from the underlying store
14132         * @param {Number} index The index of the selected item in the dropdown list
14133         */
14134         'beforeselect' : true,
14135         /**
14136          * @event select
14137          * Fires when a list item is selected
14138         * @param {Roo.bootstrap.ComboBox} combo This combo box
14139         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14140         * @param {Number} index The index of the selected item in the dropdown list
14141         */
14142         'select' : true,
14143         /**
14144          * @event beforequery
14145          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14146          * The event object passed has these properties:
14147         * @param {Roo.bootstrap.ComboBox} combo This combo box
14148         * @param {String} query The query
14149         * @param {Boolean} forceAll true to force "all" query
14150         * @param {Boolean} cancel true to cancel the query
14151         * @param {Object} e The query event object
14152         */
14153         'beforequery': true,
14154          /**
14155          * @event add
14156          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14157         * @param {Roo.bootstrap.ComboBox} combo This combo box
14158         */
14159         'add' : true,
14160         /**
14161          * @event edit
14162          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14163         * @param {Roo.bootstrap.ComboBox} combo This combo box
14164         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14165         */
14166         'edit' : true,
14167         /**
14168          * @event remove
14169          * Fires when the remove value from the combobox array
14170         * @param {Roo.bootstrap.ComboBox} combo This combo box
14171         */
14172         'remove' : true,
14173         /**
14174          * @event afterremove
14175          * Fires when the remove value from the combobox array
14176         * @param {Roo.bootstrap.ComboBox} combo This combo box
14177         */
14178         'afterremove' : true,
14179         /**
14180          * @event specialfilter
14181          * Fires when specialfilter
14182             * @param {Roo.bootstrap.ComboBox} combo This combo box
14183             */
14184         'specialfilter' : true,
14185         /**
14186          * @event tick
14187          * Fires when tick the element
14188             * @param {Roo.bootstrap.ComboBox} combo This combo box
14189             */
14190         'tick' : true,
14191         /**
14192          * @event touchviewdisplay
14193          * Fires when touch view require special display (default is using displayField)
14194             * @param {Roo.bootstrap.ComboBox} combo This combo box
14195             * @param {Object} cfg set html .
14196             */
14197         'touchviewdisplay' : true
14198         
14199     });
14200     
14201     this.item = [];
14202     this.tickItems = [];
14203     
14204     this.selectedIndex = -1;
14205     if(this.mode == 'local'){
14206         if(config.queryDelay === undefined){
14207             this.queryDelay = 10;
14208         }
14209         if(config.minChars === undefined){
14210             this.minChars = 0;
14211         }
14212     }
14213 };
14214
14215 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14216      
14217     /**
14218      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14219      * rendering into an Roo.Editor, defaults to false)
14220      */
14221     /**
14222      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14223      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14224      */
14225     /**
14226      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14227      */
14228     /**
14229      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14230      * the dropdown list (defaults to undefined, with no header element)
14231      */
14232
14233      /**
14234      * @cfg {String/Roo.Template} tpl The template to use to render the output
14235      */
14236      
14237      /**
14238      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14239      */
14240     listWidth: undefined,
14241     /**
14242      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14243      * mode = 'remote' or 'text' if mode = 'local')
14244      */
14245     displayField: undefined,
14246     
14247     /**
14248      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14249      * mode = 'remote' or 'value' if mode = 'local'). 
14250      * Note: use of a valueField requires the user make a selection
14251      * in order for a value to be mapped.
14252      */
14253     valueField: undefined,
14254     /**
14255      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14256      */
14257     modalTitle : '',
14258     
14259     /**
14260      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14261      * field's data value (defaults to the underlying DOM element's name)
14262      */
14263     hiddenName: undefined,
14264     /**
14265      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14266      */
14267     listClass: '',
14268     /**
14269      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14270      */
14271     selectedClass: 'active',
14272     
14273     /**
14274      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14275      */
14276     shadow:'sides',
14277     /**
14278      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14279      * anchor positions (defaults to 'tl-bl')
14280      */
14281     listAlign: 'tl-bl?',
14282     /**
14283      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14284      */
14285     maxHeight: 300,
14286     /**
14287      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14288      * query specified by the allQuery config option (defaults to 'query')
14289      */
14290     triggerAction: 'query',
14291     /**
14292      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14293      * (defaults to 4, does not apply if editable = false)
14294      */
14295     minChars : 4,
14296     /**
14297      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14298      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14299      */
14300     typeAhead: false,
14301     /**
14302      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14303      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14304      */
14305     queryDelay: 500,
14306     /**
14307      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14308      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14309      */
14310     pageSize: 0,
14311     /**
14312      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14313      * when editable = true (defaults to false)
14314      */
14315     selectOnFocus:false,
14316     /**
14317      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14318      */
14319     queryParam: 'query',
14320     /**
14321      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14322      * when mode = 'remote' (defaults to 'Loading...')
14323      */
14324     loadingText: 'Loading...',
14325     /**
14326      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14327      */
14328     resizable: false,
14329     /**
14330      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14331      */
14332     handleHeight : 8,
14333     /**
14334      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14335      * traditional select (defaults to true)
14336      */
14337     editable: true,
14338     /**
14339      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14340      */
14341     allQuery: '',
14342     /**
14343      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14344      */
14345     mode: 'remote',
14346     /**
14347      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14348      * listWidth has a higher value)
14349      */
14350     minListWidth : 70,
14351     /**
14352      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14353      * allow the user to set arbitrary text into the field (defaults to false)
14354      */
14355     forceSelection:false,
14356     /**
14357      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14358      * if typeAhead = true (defaults to 250)
14359      */
14360     typeAheadDelay : 250,
14361     /**
14362      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14363      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14364      */
14365     valueNotFoundText : undefined,
14366     /**
14367      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14368      */
14369     blockFocus : false,
14370     
14371     /**
14372      * @cfg {Boolean} disableClear Disable showing of clear button.
14373      */
14374     disableClear : false,
14375     /**
14376      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14377      */
14378     alwaysQuery : false,
14379     
14380     /**
14381      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14382      */
14383     multiple : false,
14384     
14385     /**
14386      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14387      */
14388     invalidClass : "has-warning",
14389     
14390     /**
14391      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14392      */
14393     validClass : "has-success",
14394     
14395     /**
14396      * @cfg {Boolean} specialFilter (true|false) special filter default false
14397      */
14398     specialFilter : false,
14399     
14400     /**
14401      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14402      */
14403     mobileTouchView : true,
14404     
14405     /**
14406      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14407      */
14408     useNativeIOS : false,
14409     
14410     /**
14411      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14412      */
14413     mobile_restrict_height : false,
14414     
14415     ios_options : false,
14416     
14417     //private
14418     addicon : false,
14419     editicon: false,
14420     
14421     page: 0,
14422     hasQuery: false,
14423     append: false,
14424     loadNext: false,
14425     autoFocus : true,
14426     tickable : false,
14427     btnPosition : 'right',
14428     triggerList : true,
14429     showToggleBtn : true,
14430     animate : true,
14431     emptyResultText: 'Empty',
14432     triggerText : 'Select',
14433     emptyTitle : '',
14434     
14435     // element that contains real text value.. (when hidden is used..)
14436     
14437     getAutoCreate : function()
14438     {   
14439         var cfg = false;
14440         //render
14441         /*
14442          * Render classic select for iso
14443          */
14444         
14445         if(Roo.isIOS && this.useNativeIOS){
14446             cfg = this.getAutoCreateNativeIOS();
14447             return cfg;
14448         }
14449         
14450         /*
14451          * Touch Devices
14452          */
14453         
14454         if(Roo.isTouch && this.mobileTouchView){
14455             cfg = this.getAutoCreateTouchView();
14456             return cfg;;
14457         }
14458         
14459         /*
14460          *  Normal ComboBox
14461          */
14462         if(!this.tickable){
14463             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14464             return cfg;
14465         }
14466         
14467         /*
14468          *  ComboBox with tickable selections
14469          */
14470              
14471         var align = this.labelAlign || this.parentLabelAlign();
14472         
14473         cfg = {
14474             cls : 'form-group roo-combobox-tickable' //input-group
14475         };
14476         
14477         var btn_text_select = '';
14478         var btn_text_done = '';
14479         var btn_text_cancel = '';
14480         
14481         if (this.btn_text_show) {
14482             btn_text_select = 'Select';
14483             btn_text_done = 'Done';
14484             btn_text_cancel = 'Cancel'; 
14485         }
14486         
14487         var buttons = {
14488             tag : 'div',
14489             cls : 'tickable-buttons',
14490             cn : [
14491                 {
14492                     tag : 'button',
14493                     type : 'button',
14494                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14495                     //html : this.triggerText
14496                     html: btn_text_select
14497                 },
14498                 {
14499                     tag : 'button',
14500                     type : 'button',
14501                     name : 'ok',
14502                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14503                     //html : 'Done'
14504                     html: btn_text_done
14505                 },
14506                 {
14507                     tag : 'button',
14508                     type : 'button',
14509                     name : 'cancel',
14510                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14511                     //html : 'Cancel'
14512                     html: btn_text_cancel
14513                 }
14514             ]
14515         };
14516         
14517         if(this.editable){
14518             buttons.cn.unshift({
14519                 tag: 'input',
14520                 cls: 'roo-select2-search-field-input'
14521             });
14522         }
14523         
14524         var _this = this;
14525         
14526         Roo.each(buttons.cn, function(c){
14527             if (_this.size) {
14528                 c.cls += ' btn-' + _this.size;
14529             }
14530
14531             if (_this.disabled) {
14532                 c.disabled = true;
14533             }
14534         });
14535         
14536         var box = {
14537             tag: 'div',
14538             style : 'display: contents',
14539             cn: [
14540                 {
14541                     tag: 'input',
14542                     type : 'hidden',
14543                     cls: 'form-hidden-field'
14544                 },
14545                 {
14546                     tag: 'ul',
14547                     cls: 'roo-select2-choices',
14548                     cn:[
14549                         {
14550                             tag: 'li',
14551                             cls: 'roo-select2-search-field',
14552                             cn: [
14553                                 buttons
14554                             ]
14555                         }
14556                     ]
14557                 }
14558             ]
14559         };
14560         
14561         var combobox = {
14562             cls: 'roo-select2-container input-group roo-select2-container-multi',
14563             cn: [
14564                 
14565                 box
14566 //                {
14567 //                    tag: 'ul',
14568 //                    cls: 'typeahead typeahead-long dropdown-menu',
14569 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14570 //                }
14571             ]
14572         };
14573         
14574         if(this.hasFeedback && !this.allowBlank){
14575             
14576             var feedback = {
14577                 tag: 'span',
14578                 cls: 'glyphicon form-control-feedback'
14579             };
14580
14581             combobox.cn.push(feedback);
14582         }
14583         
14584         var indicator = {
14585             tag : 'i',
14586             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14587             tooltip : 'This field is required'
14588         };
14589         if (Roo.bootstrap.version == 4) {
14590             indicator = {
14591                 tag : 'i',
14592                 style : 'display:none'
14593             };
14594         }
14595         if (align ==='left' && this.fieldLabel.length) {
14596             
14597             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14598             
14599             cfg.cn = [
14600                 indicator,
14601                 {
14602                     tag: 'label',
14603                     'for' :  id,
14604                     cls : 'control-label col-form-label',
14605                     html : this.fieldLabel
14606
14607                 },
14608                 {
14609                     cls : "", 
14610                     cn: [
14611                         combobox
14612                     ]
14613                 }
14614
14615             ];
14616             
14617             var labelCfg = cfg.cn[1];
14618             var contentCfg = cfg.cn[2];
14619             
14620
14621             if(this.indicatorpos == 'right'){
14622                 
14623                 cfg.cn = [
14624                     {
14625                         tag: 'label',
14626                         'for' :  id,
14627                         cls : 'control-label col-form-label',
14628                         cn : [
14629                             {
14630                                 tag : 'span',
14631                                 html : this.fieldLabel
14632                             },
14633                             indicator
14634                         ]
14635                     },
14636                     {
14637                         cls : "",
14638                         cn: [
14639                             combobox
14640                         ]
14641                     }
14642
14643                 ];
14644                 
14645                 
14646                 
14647                 labelCfg = cfg.cn[0];
14648                 contentCfg = cfg.cn[1];
14649             
14650             }
14651             
14652             if(this.labelWidth > 12){
14653                 labelCfg.style = "width: " + this.labelWidth + 'px';
14654             }
14655             
14656             if(this.labelWidth < 13 && this.labelmd == 0){
14657                 this.labelmd = this.labelWidth;
14658             }
14659             
14660             if(this.labellg > 0){
14661                 labelCfg.cls += ' col-lg-' + this.labellg;
14662                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14663             }
14664             
14665             if(this.labelmd > 0){
14666                 labelCfg.cls += ' col-md-' + this.labelmd;
14667                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14668             }
14669             
14670             if(this.labelsm > 0){
14671                 labelCfg.cls += ' col-sm-' + this.labelsm;
14672                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14673             }
14674             
14675             if(this.labelxs > 0){
14676                 labelCfg.cls += ' col-xs-' + this.labelxs;
14677                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14678             }
14679                 
14680                 
14681         } else if ( this.fieldLabel.length) {
14682 //                Roo.log(" label");
14683                  cfg.cn = [
14684                    indicator,
14685                     {
14686                         tag: 'label',
14687                         //cls : 'input-group-addon',
14688                         html : this.fieldLabel
14689                     },
14690                     combobox
14691                 ];
14692                 
14693                 if(this.indicatorpos == 'right'){
14694                     cfg.cn = [
14695                         {
14696                             tag: 'label',
14697                             //cls : 'input-group-addon',
14698                             html : this.fieldLabel
14699                         },
14700                         indicator,
14701                         combobox
14702                     ];
14703                     
14704                 }
14705
14706         } else {
14707             
14708 //                Roo.log(" no label && no align");
14709                 cfg = combobox
14710                      
14711                 
14712         }
14713          
14714         var settings=this;
14715         ['xs','sm','md','lg'].map(function(size){
14716             if (settings[size]) {
14717                 cfg.cls += ' col-' + size + '-' + settings[size];
14718             }
14719         });
14720         
14721         return cfg;
14722         
14723     },
14724     
14725     _initEventsCalled : false,
14726     
14727     // private
14728     initEvents: function()
14729     {   
14730         if (this._initEventsCalled) { // as we call render... prevent looping...
14731             return;
14732         }
14733         this._initEventsCalled = true;
14734         
14735         if (!this.store) {
14736             throw "can not find store for combo";
14737         }
14738         
14739         this.indicator = this.indicatorEl();
14740         
14741         this.store = Roo.factory(this.store, Roo.data);
14742         this.store.parent = this;
14743         
14744         // if we are building from html. then this element is so complex, that we can not really
14745         // use the rendered HTML.
14746         // so we have to trash and replace the previous code.
14747         if (Roo.XComponent.build_from_html) {
14748             // remove this element....
14749             var e = this.el.dom, k=0;
14750             while (e ) { e = e.previousSibling;  ++k;}
14751
14752             this.el.remove();
14753             
14754             this.el=false;
14755             this.rendered = false;
14756             
14757             this.render(this.parent().getChildContainer(true), k);
14758         }
14759         
14760         if(Roo.isIOS && this.useNativeIOS){
14761             this.initIOSView();
14762             return;
14763         }
14764         
14765         /*
14766          * Touch Devices
14767          */
14768         
14769         if(Roo.isTouch && this.mobileTouchView){
14770             this.initTouchView();
14771             return;
14772         }
14773         
14774         if(this.tickable){
14775             this.initTickableEvents();
14776             return;
14777         }
14778         
14779         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14780         
14781         if(this.hiddenName){
14782             
14783             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14784             
14785             this.hiddenField.dom.value =
14786                 this.hiddenValue !== undefined ? this.hiddenValue :
14787                 this.value !== undefined ? this.value : '';
14788
14789             // prevent input submission
14790             this.el.dom.removeAttribute('name');
14791             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14792              
14793              
14794         }
14795         //if(Roo.isGecko){
14796         //    this.el.dom.setAttribute('autocomplete', 'off');
14797         //}
14798         
14799         var cls = 'x-combo-list';
14800         
14801         //this.list = new Roo.Layer({
14802         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14803         //});
14804         
14805         var _this = this;
14806         
14807         (function(){
14808             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14809             _this.list.setWidth(lw);
14810         }).defer(100);
14811         
14812         this.list.on('mouseover', this.onViewOver, this);
14813         this.list.on('mousemove', this.onViewMove, this);
14814         this.list.on('scroll', this.onViewScroll, this);
14815         
14816         /*
14817         this.list.swallowEvent('mousewheel');
14818         this.assetHeight = 0;
14819
14820         if(this.title){
14821             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14822             this.assetHeight += this.header.getHeight();
14823         }
14824
14825         this.innerList = this.list.createChild({cls:cls+'-inner'});
14826         this.innerList.on('mouseover', this.onViewOver, this);
14827         this.innerList.on('mousemove', this.onViewMove, this);
14828         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14829         
14830         if(this.allowBlank && !this.pageSize && !this.disableClear){
14831             this.footer = this.list.createChild({cls:cls+'-ft'});
14832             this.pageTb = new Roo.Toolbar(this.footer);
14833            
14834         }
14835         if(this.pageSize){
14836             this.footer = this.list.createChild({cls:cls+'-ft'});
14837             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14838                     {pageSize: this.pageSize});
14839             
14840         }
14841         
14842         if (this.pageTb && this.allowBlank && !this.disableClear) {
14843             var _this = this;
14844             this.pageTb.add(new Roo.Toolbar.Fill(), {
14845                 cls: 'x-btn-icon x-btn-clear',
14846                 text: '&#160;',
14847                 handler: function()
14848                 {
14849                     _this.collapse();
14850                     _this.clearValue();
14851                     _this.onSelect(false, -1);
14852                 }
14853             });
14854         }
14855         if (this.footer) {
14856             this.assetHeight += this.footer.getHeight();
14857         }
14858         */
14859             
14860         if(!this.tpl){
14861             this.tpl = Roo.bootstrap.version == 4 ?
14862                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14863                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14864         }
14865
14866         this.view = new Roo.View(this.list, this.tpl, {
14867             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14868         });
14869         //this.view.wrapEl.setDisplayed(false);
14870         this.view.on('click', this.onViewClick, this);
14871         
14872         
14873         this.store.on('beforeload', this.onBeforeLoad, this);
14874         this.store.on('load', this.onLoad, this);
14875         this.store.on('loadexception', this.onLoadException, this);
14876         /*
14877         if(this.resizable){
14878             this.resizer = new Roo.Resizable(this.list,  {
14879                pinned:true, handles:'se'
14880             });
14881             this.resizer.on('resize', function(r, w, h){
14882                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14883                 this.listWidth = w;
14884                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14885                 this.restrictHeight();
14886             }, this);
14887             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14888         }
14889         */
14890         if(!this.editable){
14891             this.editable = true;
14892             this.setEditable(false);
14893         }
14894         
14895         /*
14896         
14897         if (typeof(this.events.add.listeners) != 'undefined') {
14898             
14899             this.addicon = this.wrap.createChild(
14900                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14901        
14902             this.addicon.on('click', function(e) {
14903                 this.fireEvent('add', this);
14904             }, this);
14905         }
14906         if (typeof(this.events.edit.listeners) != 'undefined') {
14907             
14908             this.editicon = this.wrap.createChild(
14909                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14910             if (this.addicon) {
14911                 this.editicon.setStyle('margin-left', '40px');
14912             }
14913             this.editicon.on('click', function(e) {
14914                 
14915                 // we fire even  if inothing is selected..
14916                 this.fireEvent('edit', this, this.lastData );
14917                 
14918             }, this);
14919         }
14920         */
14921         
14922         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14923             "up" : function(e){
14924                 this.inKeyMode = true;
14925                 this.selectPrev();
14926             },
14927
14928             "down" : function(e){
14929                 if(!this.isExpanded()){
14930                     this.onTriggerClick();
14931                 }else{
14932                     this.inKeyMode = true;
14933                     this.selectNext();
14934                 }
14935             },
14936
14937             "enter" : function(e){
14938 //                this.onViewClick();
14939                 //return true;
14940                 this.collapse();
14941                 
14942                 if(this.fireEvent("specialkey", this, e)){
14943                     this.onViewClick(false);
14944                 }
14945                 
14946                 return true;
14947             },
14948
14949             "esc" : function(e){
14950                 this.collapse();
14951             },
14952
14953             "tab" : function(e){
14954                 this.collapse();
14955                 
14956                 if(this.fireEvent("specialkey", this, e)){
14957                     this.onViewClick(false);
14958                 }
14959                 
14960                 return true;
14961             },
14962
14963             scope : this,
14964
14965             doRelay : function(foo, bar, hname){
14966                 if(hname == 'down' || this.scope.isExpanded()){
14967                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14968                 }
14969                 return true;
14970             },
14971
14972             forceKeyDown: true
14973         });
14974         
14975         
14976         this.queryDelay = Math.max(this.queryDelay || 10,
14977                 this.mode == 'local' ? 10 : 250);
14978         
14979         
14980         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14981         
14982         if(this.typeAhead){
14983             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14984         }
14985         if(this.editable !== false){
14986             this.inputEl().on("keyup", this.onKeyUp, this);
14987         }
14988         if(this.forceSelection){
14989             this.inputEl().on('blur', this.doForce, this);
14990         }
14991         
14992         if(this.multiple){
14993             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14994             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14995         }
14996     },
14997     
14998     initTickableEvents: function()
14999     {   
15000         this.createList();
15001         
15002         if(this.hiddenName){
15003             
15004             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15005             
15006             this.hiddenField.dom.value =
15007                 this.hiddenValue !== undefined ? this.hiddenValue :
15008                 this.value !== undefined ? this.value : '';
15009
15010             // prevent input submission
15011             this.el.dom.removeAttribute('name');
15012             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15013              
15014              
15015         }
15016         
15017 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15018         
15019         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15020         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15021         if(this.triggerList){
15022             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15023         }
15024          
15025         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15026         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15027         
15028         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15029         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15030         
15031         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15032         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15033         
15034         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15035         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15036         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15037         
15038         this.okBtn.hide();
15039         this.cancelBtn.hide();
15040         
15041         var _this = this;
15042         
15043         (function(){
15044             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15045             _this.list.setWidth(lw);
15046         }).defer(100);
15047         
15048         this.list.on('mouseover', this.onViewOver, this);
15049         this.list.on('mousemove', this.onViewMove, this);
15050         
15051         this.list.on('scroll', this.onViewScroll, this);
15052         
15053         if(!this.tpl){
15054             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15055                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15056         }
15057
15058         this.view = new Roo.View(this.list, this.tpl, {
15059             singleSelect:true,
15060             tickable:true,
15061             parent:this,
15062             store: this.store,
15063             selectedClass: this.selectedClass
15064         });
15065         
15066         //this.view.wrapEl.setDisplayed(false);
15067         this.view.on('click', this.onViewClick, this);
15068         
15069         
15070         
15071         this.store.on('beforeload', this.onBeforeLoad, this);
15072         this.store.on('load', this.onLoad, this);
15073         this.store.on('loadexception', this.onLoadException, this);
15074         
15075         if(this.editable){
15076             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15077                 "up" : function(e){
15078                     this.inKeyMode = true;
15079                     this.selectPrev();
15080                 },
15081
15082                 "down" : function(e){
15083                     this.inKeyMode = true;
15084                     this.selectNext();
15085                 },
15086
15087                 "enter" : function(e){
15088                     if(this.fireEvent("specialkey", this, e)){
15089                         this.onViewClick(false);
15090                     }
15091                     
15092                     return true;
15093                 },
15094
15095                 "esc" : function(e){
15096                     this.onTickableFooterButtonClick(e, false, false);
15097                 },
15098
15099                 "tab" : function(e){
15100                     this.fireEvent("specialkey", this, e);
15101                     
15102                     this.onTickableFooterButtonClick(e, false, false);
15103                     
15104                     return true;
15105                 },
15106
15107                 scope : this,
15108
15109                 doRelay : function(e, fn, key){
15110                     if(this.scope.isExpanded()){
15111                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15112                     }
15113                     return true;
15114                 },
15115
15116                 forceKeyDown: true
15117             });
15118         }
15119         
15120         this.queryDelay = Math.max(this.queryDelay || 10,
15121                 this.mode == 'local' ? 10 : 250);
15122         
15123         
15124         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15125         
15126         if(this.typeAhead){
15127             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15128         }
15129         
15130         if(this.editable !== false){
15131             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15132         }
15133         
15134         this.indicator = this.indicatorEl();
15135         
15136         if(this.indicator){
15137             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15138             this.indicator.hide();
15139         }
15140         
15141     },
15142
15143     onDestroy : function(){
15144         if(this.view){
15145             this.view.setStore(null);
15146             this.view.el.removeAllListeners();
15147             this.view.el.remove();
15148             this.view.purgeListeners();
15149         }
15150         if(this.list){
15151             this.list.dom.innerHTML  = '';
15152         }
15153         
15154         if(this.store){
15155             this.store.un('beforeload', this.onBeforeLoad, this);
15156             this.store.un('load', this.onLoad, this);
15157             this.store.un('loadexception', this.onLoadException, this);
15158         }
15159         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15160     },
15161
15162     // private
15163     fireKey : function(e){
15164         if(e.isNavKeyPress() && !this.list.isVisible()){
15165             this.fireEvent("specialkey", this, e);
15166         }
15167     },
15168
15169     // private
15170     onResize: function(w, h){
15171 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15172 //        
15173 //        if(typeof w != 'number'){
15174 //            // we do not handle it!?!?
15175 //            return;
15176 //        }
15177 //        var tw = this.trigger.getWidth();
15178 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15179 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15180 //        var x = w - tw;
15181 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15182 //            
15183 //        //this.trigger.setStyle('left', x+'px');
15184 //        
15185 //        if(this.list && this.listWidth === undefined){
15186 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15187 //            this.list.setWidth(lw);
15188 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15189 //        }
15190         
15191     
15192         
15193     },
15194
15195     /**
15196      * Allow or prevent the user from directly editing the field text.  If false is passed,
15197      * the user will only be able to select from the items defined in the dropdown list.  This method
15198      * is the runtime equivalent of setting the 'editable' config option at config time.
15199      * @param {Boolean} value True to allow the user to directly edit the field text
15200      */
15201     setEditable : function(value){
15202         if(value == this.editable){
15203             return;
15204         }
15205         this.editable = value;
15206         if(!value){
15207             this.inputEl().dom.setAttribute('readOnly', true);
15208             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15209             this.inputEl().addClass('x-combo-noedit');
15210         }else{
15211             this.inputEl().dom.setAttribute('readOnly', false);
15212             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15213             this.inputEl().removeClass('x-combo-noedit');
15214         }
15215     },
15216
15217     // private
15218     
15219     onBeforeLoad : function(combo,opts){
15220         if(!this.hasFocus){
15221             return;
15222         }
15223          if (!opts.add) {
15224             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15225          }
15226         this.restrictHeight();
15227         this.selectedIndex = -1;
15228     },
15229
15230     // private
15231     onLoad : function(){
15232         
15233         this.hasQuery = false;
15234         
15235         if(!this.hasFocus){
15236             return;
15237         }
15238         
15239         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15240             this.loading.hide();
15241         }
15242         
15243         if(this.store.getCount() > 0){
15244             
15245             this.expand();
15246             this.restrictHeight();
15247             if(this.lastQuery == this.allQuery){
15248                 if(this.editable && !this.tickable){
15249                     this.inputEl().dom.select();
15250                 }
15251                 
15252                 if(
15253                     !this.selectByValue(this.value, true) &&
15254                     this.autoFocus && 
15255                     (
15256                         !this.store.lastOptions ||
15257                         typeof(this.store.lastOptions.add) == 'undefined' || 
15258                         this.store.lastOptions.add != true
15259                     )
15260                 ){
15261                     this.select(0, true);
15262                 }
15263             }else{
15264                 if(this.autoFocus){
15265                     this.selectNext();
15266                 }
15267                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15268                     this.taTask.delay(this.typeAheadDelay);
15269                 }
15270             }
15271         }else{
15272             this.onEmptyResults();
15273         }
15274         
15275         //this.el.focus();
15276     },
15277     // private
15278     onLoadException : function()
15279     {
15280         this.hasQuery = false;
15281         
15282         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15283             this.loading.hide();
15284         }
15285         
15286         if(this.tickable && this.editable){
15287             return;
15288         }
15289         
15290         this.collapse();
15291         // only causes errors at present
15292         //Roo.log(this.store.reader.jsonData);
15293         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15294             // fixme
15295             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15296         //}
15297         
15298         
15299     },
15300     // private
15301     onTypeAhead : function(){
15302         if(this.store.getCount() > 0){
15303             var r = this.store.getAt(0);
15304             var newValue = r.data[this.displayField];
15305             var len = newValue.length;
15306             var selStart = this.getRawValue().length;
15307             
15308             if(selStart != len){
15309                 this.setRawValue(newValue);
15310                 this.selectText(selStart, newValue.length);
15311             }
15312         }
15313     },
15314
15315     // private
15316     onSelect : function(record, index){
15317         
15318         if(this.fireEvent('beforeselect', this, record, index) !== false){
15319         
15320             this.setFromData(index > -1 ? record.data : false);
15321             
15322             this.collapse();
15323             this.fireEvent('select', this, record, index);
15324         }
15325     },
15326
15327     /**
15328      * Returns the currently selected field value or empty string if no value is set.
15329      * @return {String} value The selected value
15330      */
15331     getValue : function()
15332     {
15333         if(Roo.isIOS && this.useNativeIOS){
15334             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15335         }
15336         
15337         if(this.multiple){
15338             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15339         }
15340         
15341         if(this.valueField){
15342             return typeof this.value != 'undefined' ? this.value : '';
15343         }else{
15344             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15345         }
15346     },
15347     
15348     getRawValue : function()
15349     {
15350         if(Roo.isIOS && this.useNativeIOS){
15351             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15352         }
15353         
15354         var v = this.inputEl().getValue();
15355         
15356         return v;
15357     },
15358
15359     /**
15360      * Clears any text/value currently set in the field
15361      */
15362     clearValue : function(){
15363         
15364         if(this.hiddenField){
15365             this.hiddenField.dom.value = '';
15366         }
15367         this.value = '';
15368         this.setRawValue('');
15369         this.lastSelectionText = '';
15370         this.lastData = false;
15371         
15372         var close = this.closeTriggerEl();
15373         
15374         if(close){
15375             close.hide();
15376         }
15377         
15378         this.validate();
15379         
15380     },
15381
15382     /**
15383      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15384      * will be displayed in the field.  If the value does not match the data value of an existing item,
15385      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15386      * Otherwise the field will be blank (although the value will still be set).
15387      * @param {String} value The value to match
15388      */
15389     setValue : function(v)
15390     {
15391         if(Roo.isIOS && this.useNativeIOS){
15392             this.setIOSValue(v);
15393             return;
15394         }
15395         
15396         if(this.multiple){
15397             this.syncValue();
15398             return;
15399         }
15400         
15401         var text = v;
15402         if(this.valueField){
15403             var r = this.findRecord(this.valueField, v);
15404             if(r){
15405                 text = r.data[this.displayField];
15406             }else if(this.valueNotFoundText !== undefined){
15407                 text = this.valueNotFoundText;
15408             }
15409         }
15410         this.lastSelectionText = text;
15411         if(this.hiddenField){
15412             this.hiddenField.dom.value = v;
15413         }
15414         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15415         this.value = v;
15416         
15417         var close = this.closeTriggerEl();
15418         
15419         if(close){
15420             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15421         }
15422         
15423         this.validate();
15424     },
15425     /**
15426      * @property {Object} the last set data for the element
15427      */
15428     
15429     lastData : false,
15430     /**
15431      * Sets the value of the field based on a object which is related to the record format for the store.
15432      * @param {Object} value the value to set as. or false on reset?
15433      */
15434     setFromData : function(o){
15435         
15436         if(this.multiple){
15437             this.addItem(o);
15438             return;
15439         }
15440             
15441         var dv = ''; // display value
15442         var vv = ''; // value value..
15443         this.lastData = o;
15444         if (this.displayField) {
15445             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15446         } else {
15447             // this is an error condition!!!
15448             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15449         }
15450         
15451         if(this.valueField){
15452             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15453         }
15454         
15455         var close = this.closeTriggerEl();
15456         
15457         if(close){
15458             if(dv.length || vv * 1 > 0){
15459                 close.show() ;
15460                 this.blockFocus=true;
15461             } else {
15462                 close.hide();
15463             }             
15464         }
15465         
15466         if(this.hiddenField){
15467             this.hiddenField.dom.value = vv;
15468             
15469             this.lastSelectionText = dv;
15470             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15471             this.value = vv;
15472             return;
15473         }
15474         // no hidden field.. - we store the value in 'value', but still display
15475         // display field!!!!
15476         this.lastSelectionText = dv;
15477         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15478         this.value = vv;
15479         
15480         
15481         
15482     },
15483     // private
15484     reset : function(){
15485         // overridden so that last data is reset..
15486         
15487         if(this.multiple){
15488             this.clearItem();
15489             return;
15490         }
15491         
15492         this.setValue(this.originalValue);
15493         //this.clearInvalid();
15494         this.lastData = false;
15495         if (this.view) {
15496             this.view.clearSelections();
15497         }
15498         
15499         this.validate();
15500     },
15501     // private
15502     findRecord : function(prop, value){
15503         var record;
15504         if(this.store.getCount() > 0){
15505             this.store.each(function(r){
15506                 if(r.data[prop] == value){
15507                     record = r;
15508                     return false;
15509                 }
15510                 return true;
15511             });
15512         }
15513         return record;
15514     },
15515     
15516     getName: function()
15517     {
15518         // returns hidden if it's set..
15519         if (!this.rendered) {return ''};
15520         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15521         
15522     },
15523     // private
15524     onViewMove : function(e, t){
15525         this.inKeyMode = false;
15526     },
15527
15528     // private
15529     onViewOver : function(e, t){
15530         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15531             return;
15532         }
15533         var item = this.view.findItemFromChild(t);
15534         
15535         if(item){
15536             var index = this.view.indexOf(item);
15537             this.select(index, false);
15538         }
15539     },
15540
15541     // private
15542     onViewClick : function(view, doFocus, el, e)
15543     {
15544         var index = this.view.getSelectedIndexes()[0];
15545         
15546         var r = this.store.getAt(index);
15547         
15548         if(this.tickable){
15549             
15550             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15551                 return;
15552             }
15553             
15554             var rm = false;
15555             var _this = this;
15556             
15557             Roo.each(this.tickItems, function(v,k){
15558                 
15559                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15560                     Roo.log(v);
15561                     _this.tickItems.splice(k, 1);
15562                     
15563                     if(typeof(e) == 'undefined' && view == false){
15564                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15565                     }
15566                     
15567                     rm = true;
15568                     return;
15569                 }
15570             });
15571             
15572             if(rm){
15573                 return;
15574             }
15575             
15576             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15577                 this.tickItems.push(r.data);
15578             }
15579             
15580             if(typeof(e) == 'undefined' && view == false){
15581                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15582             }
15583                     
15584             return;
15585         }
15586         
15587         if(r){
15588             this.onSelect(r, index);
15589         }
15590         if(doFocus !== false && !this.blockFocus){
15591             this.inputEl().focus();
15592         }
15593     },
15594
15595     // private
15596     restrictHeight : function(){
15597         //this.innerList.dom.style.height = '';
15598         //var inner = this.innerList.dom;
15599         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15600         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15601         //this.list.beginUpdate();
15602         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15603         this.list.alignTo(this.inputEl(), this.listAlign);
15604         this.list.alignTo(this.inputEl(), this.listAlign);
15605         //this.list.endUpdate();
15606     },
15607
15608     // private
15609     onEmptyResults : function(){
15610         
15611         if(this.tickable && this.editable){
15612             this.hasFocus = false;
15613             this.restrictHeight();
15614             return;
15615         }
15616         
15617         this.collapse();
15618     },
15619
15620     /**
15621      * Returns true if the dropdown list is expanded, else false.
15622      */
15623     isExpanded : function(){
15624         return this.list.isVisible();
15625     },
15626
15627     /**
15628      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15629      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15630      * @param {String} value The data value of the item to select
15631      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15632      * selected item if it is not currently in view (defaults to true)
15633      * @return {Boolean} True if the value matched an item in the list, else false
15634      */
15635     selectByValue : function(v, scrollIntoView){
15636         if(v !== undefined && v !== null){
15637             var r = this.findRecord(this.valueField || this.displayField, v);
15638             if(r){
15639                 this.select(this.store.indexOf(r), scrollIntoView);
15640                 return true;
15641             }
15642         }
15643         return false;
15644     },
15645
15646     /**
15647      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15649      * @param {Number} index The zero-based index of the list item to select
15650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15651      * selected item if it is not currently in view (defaults to true)
15652      */
15653     select : function(index, scrollIntoView){
15654         this.selectedIndex = index;
15655         this.view.select(index);
15656         if(scrollIntoView !== false){
15657             var el = this.view.getNode(index);
15658             /*
15659              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15660              */
15661             if(el){
15662                 this.list.scrollChildIntoView(el, false);
15663             }
15664         }
15665     },
15666
15667     // private
15668     selectNext : function(){
15669         var ct = this.store.getCount();
15670         if(ct > 0){
15671             if(this.selectedIndex == -1){
15672                 this.select(0);
15673             }else if(this.selectedIndex < ct-1){
15674                 this.select(this.selectedIndex+1);
15675             }
15676         }
15677     },
15678
15679     // private
15680     selectPrev : function(){
15681         var ct = this.store.getCount();
15682         if(ct > 0){
15683             if(this.selectedIndex == -1){
15684                 this.select(0);
15685             }else if(this.selectedIndex != 0){
15686                 this.select(this.selectedIndex-1);
15687             }
15688         }
15689     },
15690
15691     // private
15692     onKeyUp : function(e){
15693         if(this.editable !== false && !e.isSpecialKey()){
15694             this.lastKey = e.getKey();
15695             this.dqTask.delay(this.queryDelay);
15696         }
15697     },
15698
15699     // private
15700     validateBlur : function(){
15701         return !this.list || !this.list.isVisible();   
15702     },
15703
15704     // private
15705     initQuery : function(){
15706         
15707         var v = this.getRawValue();
15708         
15709         if(this.tickable && this.editable){
15710             v = this.tickableInputEl().getValue();
15711         }
15712         
15713         this.doQuery(v);
15714     },
15715
15716     // private
15717     doForce : function(){
15718         if(this.inputEl().dom.value.length > 0){
15719             this.inputEl().dom.value =
15720                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15721              
15722         }
15723     },
15724
15725     /**
15726      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15727      * query allowing the query action to be canceled if needed.
15728      * @param {String} query The SQL query to execute
15729      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15730      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15731      * saved in the current store (defaults to false)
15732      */
15733     doQuery : function(q, forceAll){
15734         
15735         if(q === undefined || q === null){
15736             q = '';
15737         }
15738         var qe = {
15739             query: q,
15740             forceAll: forceAll,
15741             combo: this,
15742             cancel:false
15743         };
15744         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15745             return false;
15746         }
15747         q = qe.query;
15748         
15749         forceAll = qe.forceAll;
15750         if(forceAll === true || (q.length >= this.minChars)){
15751             
15752             this.hasQuery = true;
15753             
15754             if(this.lastQuery != q || this.alwaysQuery){
15755                 this.lastQuery = q;
15756                 if(this.mode == 'local'){
15757                     this.selectedIndex = -1;
15758                     if(forceAll){
15759                         this.store.clearFilter();
15760                     }else{
15761                         
15762                         if(this.specialFilter){
15763                             this.fireEvent('specialfilter', this);
15764                             this.onLoad();
15765                             return;
15766                         }
15767                         
15768                         this.store.filter(this.displayField, q);
15769                     }
15770                     
15771                     this.store.fireEvent("datachanged", this.store);
15772                     
15773                     this.onLoad();
15774                     
15775                     
15776                 }else{
15777                     
15778                     this.store.baseParams[this.queryParam] = q;
15779                     
15780                     var options = {params : this.getParams(q)};
15781                     
15782                     if(this.loadNext){
15783                         options.add = true;
15784                         options.params.start = this.page * this.pageSize;
15785                     }
15786                     
15787                     this.store.load(options);
15788                     
15789                     /*
15790                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15791                      *  we should expand the list on onLoad
15792                      *  so command out it
15793                      */
15794 //                    this.expand();
15795                 }
15796             }else{
15797                 this.selectedIndex = -1;
15798                 this.onLoad();   
15799             }
15800         }
15801         
15802         this.loadNext = false;
15803     },
15804     
15805     // private
15806     getParams : function(q){
15807         var p = {};
15808         //p[this.queryParam] = q;
15809         
15810         if(this.pageSize){
15811             p.start = 0;
15812             p.limit = this.pageSize;
15813         }
15814         return p;
15815     },
15816
15817     /**
15818      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15819      */
15820     collapse : function(){
15821         if(!this.isExpanded()){
15822             return;
15823         }
15824         
15825         this.list.hide();
15826         
15827         this.hasFocus = false;
15828         
15829         if(this.tickable){
15830             this.okBtn.hide();
15831             this.cancelBtn.hide();
15832             this.trigger.show();
15833             
15834             if(this.editable){
15835                 this.tickableInputEl().dom.value = '';
15836                 this.tickableInputEl().blur();
15837             }
15838             
15839         }
15840         
15841         Roo.get(document).un('mousedown', this.collapseIf, this);
15842         Roo.get(document).un('mousewheel', this.collapseIf, this);
15843         if (!this.editable) {
15844             Roo.get(document).un('keydown', this.listKeyPress, this);
15845         }
15846         this.fireEvent('collapse', this);
15847         
15848         this.validate();
15849     },
15850
15851     // private
15852     collapseIf : function(e){
15853         var in_combo  = e.within(this.el);
15854         var in_list =  e.within(this.list);
15855         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15856         
15857         if (in_combo || in_list || is_list) {
15858             //e.stopPropagation();
15859             return;
15860         }
15861         
15862         if(this.tickable){
15863             this.onTickableFooterButtonClick(e, false, false);
15864         }
15865
15866         this.collapse();
15867         
15868     },
15869
15870     /**
15871      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15872      */
15873     expand : function(){
15874        
15875         if(this.isExpanded() || !this.hasFocus){
15876             return;
15877         }
15878         
15879         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15880         this.list.setWidth(lw);
15881         
15882         Roo.log('expand');
15883         
15884         this.list.show();
15885         
15886         this.restrictHeight();
15887         
15888         if(this.tickable){
15889             
15890             this.tickItems = Roo.apply([], this.item);
15891             
15892             this.okBtn.show();
15893             this.cancelBtn.show();
15894             this.trigger.hide();
15895             
15896             if(this.editable){
15897                 this.tickableInputEl().focus();
15898             }
15899             
15900         }
15901         
15902         Roo.get(document).on('mousedown', this.collapseIf, this);
15903         Roo.get(document).on('mousewheel', this.collapseIf, this);
15904         if (!this.editable) {
15905             Roo.get(document).on('keydown', this.listKeyPress, this);
15906         }
15907         
15908         this.fireEvent('expand', this);
15909     },
15910
15911     // private
15912     // Implements the default empty TriggerField.onTriggerClick function
15913     onTriggerClick : function(e)
15914     {
15915         Roo.log('trigger click');
15916         
15917         if(this.disabled || !this.triggerList){
15918             return;
15919         }
15920         
15921         this.page = 0;
15922         this.loadNext = false;
15923         
15924         if(this.isExpanded()){
15925             this.collapse();
15926             if (!this.blockFocus) {
15927                 this.inputEl().focus();
15928             }
15929             
15930         }else {
15931             this.hasFocus = true;
15932             if(this.triggerAction == 'all') {
15933                 this.doQuery(this.allQuery, true);
15934             } else {
15935                 this.doQuery(this.getRawValue());
15936             }
15937             if (!this.blockFocus) {
15938                 this.inputEl().focus();
15939             }
15940         }
15941     },
15942     
15943     onTickableTriggerClick : function(e)
15944     {
15945         if(this.disabled){
15946             return;
15947         }
15948         
15949         this.page = 0;
15950         this.loadNext = false;
15951         this.hasFocus = true;
15952         
15953         if(this.triggerAction == 'all') {
15954             this.doQuery(this.allQuery, true);
15955         } else {
15956             this.doQuery(this.getRawValue());
15957         }
15958     },
15959     
15960     onSearchFieldClick : function(e)
15961     {
15962         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15963             this.onTickableFooterButtonClick(e, false, false);
15964             return;
15965         }
15966         
15967         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15968             return;
15969         }
15970         
15971         this.page = 0;
15972         this.loadNext = false;
15973         this.hasFocus = true;
15974         
15975         if(this.triggerAction == 'all') {
15976             this.doQuery(this.allQuery, true);
15977         } else {
15978             this.doQuery(this.getRawValue());
15979         }
15980     },
15981     
15982     listKeyPress : function(e)
15983     {
15984         //Roo.log('listkeypress');
15985         // scroll to first matching element based on key pres..
15986         if (e.isSpecialKey()) {
15987             return false;
15988         }
15989         var k = String.fromCharCode(e.getKey()).toUpperCase();
15990         //Roo.log(k);
15991         var match  = false;
15992         var csel = this.view.getSelectedNodes();
15993         var cselitem = false;
15994         if (csel.length) {
15995             var ix = this.view.indexOf(csel[0]);
15996             cselitem  = this.store.getAt(ix);
15997             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15998                 cselitem = false;
15999             }
16000             
16001         }
16002         
16003         this.store.each(function(v) { 
16004             if (cselitem) {
16005                 // start at existing selection.
16006                 if (cselitem.id == v.id) {
16007                     cselitem = false;
16008                 }
16009                 return true;
16010             }
16011                 
16012             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16013                 match = this.store.indexOf(v);
16014                 return false;
16015             }
16016             return true;
16017         }, this);
16018         
16019         if (match === false) {
16020             return true; // no more action?
16021         }
16022         // scroll to?
16023         this.view.select(match);
16024         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16025         sn.scrollIntoView(sn.dom.parentNode, false);
16026     },
16027     
16028     onViewScroll : function(e, t){
16029         
16030         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){
16031             return;
16032         }
16033         
16034         this.hasQuery = true;
16035         
16036         this.loading = this.list.select('.loading', true).first();
16037         
16038         if(this.loading === null){
16039             this.list.createChild({
16040                 tag: 'div',
16041                 cls: 'loading roo-select2-more-results roo-select2-active',
16042                 html: 'Loading more results...'
16043             });
16044             
16045             this.loading = this.list.select('.loading', true).first();
16046             
16047             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16048             
16049             this.loading.hide();
16050         }
16051         
16052         this.loading.show();
16053         
16054         var _combo = this;
16055         
16056         this.page++;
16057         this.loadNext = true;
16058         
16059         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16060         
16061         return;
16062     },
16063     
16064     addItem : function(o)
16065     {   
16066         var dv = ''; // display value
16067         
16068         if (this.displayField) {
16069             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16070         } else {
16071             // this is an error condition!!!
16072             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16073         }
16074         
16075         if(!dv.length){
16076             return;
16077         }
16078         
16079         var choice = this.choices.createChild({
16080             tag: 'li',
16081             cls: 'roo-select2-search-choice',
16082             cn: [
16083                 {
16084                     tag: 'div',
16085                     html: dv
16086                 },
16087                 {
16088                     tag: 'a',
16089                     href: '#',
16090                     cls: 'roo-select2-search-choice-close fa fa-times',
16091                     tabindex: '-1'
16092                 }
16093             ]
16094             
16095         }, this.searchField);
16096         
16097         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16098         
16099         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16100         
16101         this.item.push(o);
16102         
16103         this.lastData = o;
16104         
16105         this.syncValue();
16106         
16107         this.inputEl().dom.value = '';
16108         
16109         this.validate();
16110     },
16111     
16112     onRemoveItem : function(e, _self, o)
16113     {
16114         e.preventDefault();
16115         
16116         this.lastItem = Roo.apply([], this.item);
16117         
16118         var index = this.item.indexOf(o.data) * 1;
16119         
16120         if( index < 0){
16121             Roo.log('not this item?!');
16122             return;
16123         }
16124         
16125         this.item.splice(index, 1);
16126         o.item.remove();
16127         
16128         this.syncValue();
16129         
16130         this.fireEvent('remove', this, e);
16131         
16132         this.validate();
16133         
16134     },
16135     
16136     syncValue : function()
16137     {
16138         if(!this.item.length){
16139             this.clearValue();
16140             return;
16141         }
16142             
16143         var value = [];
16144         var _this = this;
16145         Roo.each(this.item, function(i){
16146             if(_this.valueField){
16147                 value.push(i[_this.valueField]);
16148                 return;
16149             }
16150
16151             value.push(i);
16152         });
16153
16154         this.value = value.join(',');
16155
16156         if(this.hiddenField){
16157             this.hiddenField.dom.value = this.value;
16158         }
16159         
16160         this.store.fireEvent("datachanged", this.store);
16161         
16162         this.validate();
16163     },
16164     
16165     clearItem : function()
16166     {
16167         if(!this.multiple){
16168             return;
16169         }
16170         
16171         this.item = [];
16172         
16173         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16174            c.remove();
16175         });
16176         
16177         this.syncValue();
16178         
16179         this.validate();
16180         
16181         if(this.tickable && !Roo.isTouch){
16182             this.view.refresh();
16183         }
16184     },
16185     
16186     inputEl: function ()
16187     {
16188         if(Roo.isIOS && this.useNativeIOS){
16189             return this.el.select('select.roo-ios-select', true).first();
16190         }
16191         
16192         if(Roo.isTouch && this.mobileTouchView){
16193             return this.el.select('input.form-control',true).first();
16194         }
16195         
16196         if(this.tickable){
16197             return this.searchField;
16198         }
16199         
16200         return this.el.select('input.form-control',true).first();
16201     },
16202     
16203     onTickableFooterButtonClick : function(e, btn, el)
16204     {
16205         e.preventDefault();
16206         
16207         this.lastItem = Roo.apply([], this.item);
16208         
16209         if(btn && btn.name == 'cancel'){
16210             this.tickItems = Roo.apply([], this.item);
16211             this.collapse();
16212             return;
16213         }
16214         
16215         this.clearItem();
16216         
16217         var _this = this;
16218         
16219         Roo.each(this.tickItems, function(o){
16220             _this.addItem(o);
16221         });
16222         
16223         this.collapse();
16224         
16225     },
16226     
16227     validate : function()
16228     {
16229         if(this.getVisibilityEl().hasClass('hidden')){
16230             return true;
16231         }
16232         
16233         var v = this.getRawValue();
16234         
16235         if(this.multiple){
16236             v = this.getValue();
16237         }
16238         
16239         if(this.disabled || this.allowBlank || v.length){
16240             this.markValid();
16241             return true;
16242         }
16243         
16244         this.markInvalid();
16245         return false;
16246     },
16247     
16248     tickableInputEl : function()
16249     {
16250         if(!this.tickable || !this.editable){
16251             return this.inputEl();
16252         }
16253         
16254         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16255     },
16256     
16257     
16258     getAutoCreateTouchView : function()
16259     {
16260         var id = Roo.id();
16261         
16262         var cfg = {
16263             cls: 'form-group' //input-group
16264         };
16265         
16266         var input =  {
16267             tag: 'input',
16268             id : id,
16269             type : this.inputType,
16270             cls : 'form-control x-combo-noedit',
16271             autocomplete: 'new-password',
16272             placeholder : this.placeholder || '',
16273             readonly : true
16274         };
16275         
16276         if (this.name) {
16277             input.name = this.name;
16278         }
16279         
16280         if (this.size) {
16281             input.cls += ' input-' + this.size;
16282         }
16283         
16284         if (this.disabled) {
16285             input.disabled = true;
16286         }
16287         
16288         var inputblock = {
16289             cls : '',
16290             cn : [
16291                 input
16292             ]
16293         };
16294         
16295         if(this.before){
16296             inputblock.cls += ' input-group';
16297             
16298             inputblock.cn.unshift({
16299                 tag :'span',
16300                 cls : 'input-group-addon input-group-prepend input-group-text',
16301                 html : this.before
16302             });
16303         }
16304         
16305         if(this.removable && !this.multiple){
16306             inputblock.cls += ' roo-removable';
16307             
16308             inputblock.cn.push({
16309                 tag: 'button',
16310                 html : 'x',
16311                 cls : 'roo-combo-removable-btn close'
16312             });
16313         }
16314
16315         if(this.hasFeedback && !this.allowBlank){
16316             
16317             inputblock.cls += ' has-feedback';
16318             
16319             inputblock.cn.push({
16320                 tag: 'span',
16321                 cls: 'glyphicon form-control-feedback'
16322             });
16323             
16324         }
16325         
16326         if (this.after) {
16327             
16328             inputblock.cls += (this.before) ? '' : ' input-group';
16329             
16330             inputblock.cn.push({
16331                 tag :'span',
16332                 cls : 'input-group-addon input-group-append input-group-text',
16333                 html : this.after
16334             });
16335         }
16336
16337         
16338         var ibwrap = inputblock;
16339         
16340         if(this.multiple){
16341             ibwrap = {
16342                 tag: 'ul',
16343                 cls: 'roo-select2-choices',
16344                 cn:[
16345                     {
16346                         tag: 'li',
16347                         cls: 'roo-select2-search-field',
16348                         cn: [
16349
16350                             inputblock
16351                         ]
16352                     }
16353                 ]
16354             };
16355         
16356             
16357         }
16358         
16359         var combobox = {
16360             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16361             cn: [
16362                 {
16363                     tag: 'input',
16364                     type : 'hidden',
16365                     cls: 'form-hidden-field'
16366                 },
16367                 ibwrap
16368             ]
16369         };
16370         
16371         if(!this.multiple && this.showToggleBtn){
16372             
16373             var caret = {
16374                 cls: 'caret'
16375             };
16376             
16377             if (this.caret != false) {
16378                 caret = {
16379                      tag: 'i',
16380                      cls: 'fa fa-' + this.caret
16381                 };
16382                 
16383             }
16384             
16385             combobox.cn.push({
16386                 tag :'span',
16387                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16388                 cn : [
16389                     Roo.bootstrap.version == 3 ? caret : '',
16390                     {
16391                         tag: 'span',
16392                         cls: 'combobox-clear',
16393                         cn  : [
16394                             {
16395                                 tag : 'i',
16396                                 cls: 'icon-remove'
16397                             }
16398                         ]
16399                     }
16400                 ]
16401
16402             })
16403         }
16404         
16405         if(this.multiple){
16406             combobox.cls += ' roo-select2-container-multi';
16407         }
16408         
16409         var align = this.labelAlign || this.parentLabelAlign();
16410         
16411         if (align ==='left' && this.fieldLabel.length) {
16412
16413             cfg.cn = [
16414                 {
16415                    tag : 'i',
16416                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16417                    tooltip : 'This field is required'
16418                 },
16419                 {
16420                     tag: 'label',
16421                     cls : 'control-label col-form-label',
16422                     html : this.fieldLabel
16423
16424                 },
16425                 {
16426                     cls : '', 
16427                     cn: [
16428                         combobox
16429                     ]
16430                 }
16431             ];
16432             
16433             var labelCfg = cfg.cn[1];
16434             var contentCfg = cfg.cn[2];
16435             
16436
16437             if(this.indicatorpos == 'right'){
16438                 cfg.cn = [
16439                     {
16440                         tag: 'label',
16441                         'for' :  id,
16442                         cls : 'control-label col-form-label',
16443                         cn : [
16444                             {
16445                                 tag : 'span',
16446                                 html : this.fieldLabel
16447                             },
16448                             {
16449                                 tag : 'i',
16450                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16451                                 tooltip : 'This field is required'
16452                             }
16453                         ]
16454                     },
16455                     {
16456                         cls : "",
16457                         cn: [
16458                             combobox
16459                         ]
16460                     }
16461
16462                 ];
16463                 
16464                 labelCfg = cfg.cn[0];
16465                 contentCfg = cfg.cn[1];
16466             }
16467             
16468            
16469             
16470             if(this.labelWidth > 12){
16471                 labelCfg.style = "width: " + this.labelWidth + 'px';
16472             }
16473             
16474             if(this.labelWidth < 13 && this.labelmd == 0){
16475                 this.labelmd = this.labelWidth;
16476             }
16477             
16478             if(this.labellg > 0){
16479                 labelCfg.cls += ' col-lg-' + this.labellg;
16480                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16481             }
16482             
16483             if(this.labelmd > 0){
16484                 labelCfg.cls += ' col-md-' + this.labelmd;
16485                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16486             }
16487             
16488             if(this.labelsm > 0){
16489                 labelCfg.cls += ' col-sm-' + this.labelsm;
16490                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16491             }
16492             
16493             if(this.labelxs > 0){
16494                 labelCfg.cls += ' col-xs-' + this.labelxs;
16495                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16496             }
16497                 
16498                 
16499         } else if ( this.fieldLabel.length) {
16500             cfg.cn = [
16501                 {
16502                    tag : 'i',
16503                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16504                    tooltip : 'This field is required'
16505                 },
16506                 {
16507                     tag: 'label',
16508                     cls : 'control-label',
16509                     html : this.fieldLabel
16510
16511                 },
16512                 {
16513                     cls : '', 
16514                     cn: [
16515                         combobox
16516                     ]
16517                 }
16518             ];
16519             
16520             if(this.indicatorpos == 'right'){
16521                 cfg.cn = [
16522                     {
16523                         tag: 'label',
16524                         cls : 'control-label',
16525                         html : this.fieldLabel,
16526                         cn : [
16527                             {
16528                                tag : 'i',
16529                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16530                                tooltip : 'This field is required'
16531                             }
16532                         ]
16533                     },
16534                     {
16535                         cls : '', 
16536                         cn: [
16537                             combobox
16538                         ]
16539                     }
16540                 ];
16541             }
16542         } else {
16543             cfg.cn = combobox;    
16544         }
16545         
16546         
16547         var settings = this;
16548         
16549         ['xs','sm','md','lg'].map(function(size){
16550             if (settings[size]) {
16551                 cfg.cls += ' col-' + size + '-' + settings[size];
16552             }
16553         });
16554         
16555         return cfg;
16556     },
16557     
16558     initTouchView : function()
16559     {
16560         this.renderTouchView();
16561         
16562         this.touchViewEl.on('scroll', function(){
16563             this.el.dom.scrollTop = 0;
16564         }, this);
16565         
16566         this.originalValue = this.getValue();
16567         
16568         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16569         
16570         this.inputEl().on("click", this.showTouchView, this);
16571         if (this.triggerEl) {
16572             this.triggerEl.on("click", this.showTouchView, this);
16573         }
16574         
16575         
16576         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16577         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16578         
16579         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16580         
16581         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16582         this.store.on('load', this.onTouchViewLoad, this);
16583         this.store.on('loadexception', this.onTouchViewLoadException, this);
16584         
16585         if(this.hiddenName){
16586             
16587             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16588             
16589             this.hiddenField.dom.value =
16590                 this.hiddenValue !== undefined ? this.hiddenValue :
16591                 this.value !== undefined ? this.value : '';
16592         
16593             this.el.dom.removeAttribute('name');
16594             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16595         }
16596         
16597         if(this.multiple){
16598             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16599             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16600         }
16601         
16602         if(this.removable && !this.multiple){
16603             var close = this.closeTriggerEl();
16604             if(close){
16605                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16606                 close.on('click', this.removeBtnClick, this, close);
16607             }
16608         }
16609         /*
16610          * fix the bug in Safari iOS8
16611          */
16612         this.inputEl().on("focus", function(e){
16613             document.activeElement.blur();
16614         }, this);
16615         
16616         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16617         
16618         return;
16619         
16620         
16621     },
16622     
16623     renderTouchView : function()
16624     {
16625         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16626         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16627         
16628         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16629         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16630         
16631         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16632         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16633         this.touchViewBodyEl.setStyle('overflow', 'auto');
16634         
16635         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16636         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16637         
16638         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16639         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16640         
16641     },
16642     
16643     showTouchView : function()
16644     {
16645         if(this.disabled){
16646             return;
16647         }
16648         
16649         this.touchViewHeaderEl.hide();
16650
16651         if(this.modalTitle.length){
16652             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16653             this.touchViewHeaderEl.show();
16654         }
16655
16656         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16657         this.touchViewEl.show();
16658
16659         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16660         
16661         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16662         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16663
16664         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16665
16666         if(this.modalTitle.length){
16667             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16668         }
16669         
16670         this.touchViewBodyEl.setHeight(bodyHeight);
16671
16672         if(this.animate){
16673             var _this = this;
16674             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16675         }else{
16676             this.touchViewEl.addClass('in');
16677         }
16678         
16679         if(this._touchViewMask){
16680             Roo.get(document.body).addClass("x-body-masked");
16681             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16682             this._touchViewMask.setStyle('z-index', 10000);
16683             this._touchViewMask.addClass('show');
16684         }
16685         
16686         this.doTouchViewQuery();
16687         
16688     },
16689     
16690     hideTouchView : function()
16691     {
16692         this.touchViewEl.removeClass('in');
16693
16694         if(this.animate){
16695             var _this = this;
16696             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16697         }else{
16698             this.touchViewEl.setStyle('display', 'none');
16699         }
16700         
16701         if(this._touchViewMask){
16702             this._touchViewMask.removeClass('show');
16703             Roo.get(document.body).removeClass("x-body-masked");
16704         }
16705     },
16706     
16707     setTouchViewValue : function()
16708     {
16709         if(this.multiple){
16710             this.clearItem();
16711         
16712             var _this = this;
16713
16714             Roo.each(this.tickItems, function(o){
16715                 this.addItem(o);
16716             }, this);
16717         }
16718         
16719         this.hideTouchView();
16720     },
16721     
16722     doTouchViewQuery : function()
16723     {
16724         var qe = {
16725             query: '',
16726             forceAll: true,
16727             combo: this,
16728             cancel:false
16729         };
16730         
16731         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16732             return false;
16733         }
16734         
16735         if(!this.alwaysQuery || this.mode == 'local'){
16736             this.onTouchViewLoad();
16737             return;
16738         }
16739         
16740         this.store.load();
16741     },
16742     
16743     onTouchViewBeforeLoad : function(combo,opts)
16744     {
16745         return;
16746     },
16747
16748     // private
16749     onTouchViewLoad : function()
16750     {
16751         if(this.store.getCount() < 1){
16752             this.onTouchViewEmptyResults();
16753             return;
16754         }
16755         
16756         this.clearTouchView();
16757         
16758         var rawValue = this.getRawValue();
16759         
16760         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16761         
16762         this.tickItems = [];
16763         
16764         this.store.data.each(function(d, rowIndex){
16765             var row = this.touchViewListGroup.createChild(template);
16766             
16767             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16768                 row.addClass(d.data.cls);
16769             }
16770             
16771             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16772                 var cfg = {
16773                     data : d.data,
16774                     html : d.data[this.displayField]
16775                 };
16776                 
16777                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16778                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16779                 }
16780             }
16781             row.removeClass('selected');
16782             if(!this.multiple && this.valueField &&
16783                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16784             {
16785                 // radio buttons..
16786                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16787                 row.addClass('selected');
16788             }
16789             
16790             if(this.multiple && this.valueField &&
16791                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16792             {
16793                 
16794                 // checkboxes...
16795                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16796                 this.tickItems.push(d.data);
16797             }
16798             
16799             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16800             
16801         }, this);
16802         
16803         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16804         
16805         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16806
16807         if(this.modalTitle.length){
16808             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16809         }
16810
16811         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16812         
16813         if(this.mobile_restrict_height && listHeight < bodyHeight){
16814             this.touchViewBodyEl.setHeight(listHeight);
16815         }
16816         
16817         var _this = this;
16818         
16819         if(firstChecked && listHeight > bodyHeight){
16820             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16821         }
16822         
16823     },
16824     
16825     onTouchViewLoadException : function()
16826     {
16827         this.hideTouchView();
16828     },
16829     
16830     onTouchViewEmptyResults : function()
16831     {
16832         this.clearTouchView();
16833         
16834         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16835         
16836         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16837         
16838     },
16839     
16840     clearTouchView : function()
16841     {
16842         this.touchViewListGroup.dom.innerHTML = '';
16843     },
16844     
16845     onTouchViewClick : function(e, el, o)
16846     {
16847         e.preventDefault();
16848         
16849         var row = o.row;
16850         var rowIndex = o.rowIndex;
16851         
16852         var r = this.store.getAt(rowIndex);
16853         
16854         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16855             
16856             if(!this.multiple){
16857                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16858                     c.dom.removeAttribute('checked');
16859                 }, this);
16860
16861                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16862
16863                 this.setFromData(r.data);
16864
16865                 var close = this.closeTriggerEl();
16866
16867                 if(close){
16868                     close.show();
16869                 }
16870
16871                 this.hideTouchView();
16872
16873                 this.fireEvent('select', this, r, rowIndex);
16874
16875                 return;
16876             }
16877
16878             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16879                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16880                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16881                 return;
16882             }
16883
16884             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16885             this.addItem(r.data);
16886             this.tickItems.push(r.data);
16887         }
16888     },
16889     
16890     getAutoCreateNativeIOS : function()
16891     {
16892         var cfg = {
16893             cls: 'form-group' //input-group,
16894         };
16895         
16896         var combobox =  {
16897             tag: 'select',
16898             cls : 'roo-ios-select'
16899         };
16900         
16901         if (this.name) {
16902             combobox.name = this.name;
16903         }
16904         
16905         if (this.disabled) {
16906             combobox.disabled = true;
16907         }
16908         
16909         var settings = this;
16910         
16911         ['xs','sm','md','lg'].map(function(size){
16912             if (settings[size]) {
16913                 cfg.cls += ' col-' + size + '-' + settings[size];
16914             }
16915         });
16916         
16917         cfg.cn = combobox;
16918         
16919         return cfg;
16920         
16921     },
16922     
16923     initIOSView : function()
16924     {
16925         this.store.on('load', this.onIOSViewLoad, this);
16926         
16927         return;
16928     },
16929     
16930     onIOSViewLoad : function()
16931     {
16932         if(this.store.getCount() < 1){
16933             return;
16934         }
16935         
16936         this.clearIOSView();
16937         
16938         if(this.allowBlank) {
16939             
16940             var default_text = '-- SELECT --';
16941             
16942             if(this.placeholder.length){
16943                 default_text = this.placeholder;
16944             }
16945             
16946             if(this.emptyTitle.length){
16947                 default_text += ' - ' + this.emptyTitle + ' -';
16948             }
16949             
16950             var opt = this.inputEl().createChild({
16951                 tag: 'option',
16952                 value : 0,
16953                 html : default_text
16954             });
16955             
16956             var o = {};
16957             o[this.valueField] = 0;
16958             o[this.displayField] = default_text;
16959             
16960             this.ios_options.push({
16961                 data : o,
16962                 el : opt
16963             });
16964             
16965         }
16966         
16967         this.store.data.each(function(d, rowIndex){
16968             
16969             var html = '';
16970             
16971             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16972                 html = d.data[this.displayField];
16973             }
16974             
16975             var value = '';
16976             
16977             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16978                 value = d.data[this.valueField];
16979             }
16980             
16981             var option = {
16982                 tag: 'option',
16983                 value : value,
16984                 html : html
16985             };
16986             
16987             if(this.value == d.data[this.valueField]){
16988                 option['selected'] = true;
16989             }
16990             
16991             var opt = this.inputEl().createChild(option);
16992             
16993             this.ios_options.push({
16994                 data : d.data,
16995                 el : opt
16996             });
16997             
16998         }, this);
16999         
17000         this.inputEl().on('change', function(){
17001            this.fireEvent('select', this);
17002         }, this);
17003         
17004     },
17005     
17006     clearIOSView: function()
17007     {
17008         this.inputEl().dom.innerHTML = '';
17009         
17010         this.ios_options = [];
17011     },
17012     
17013     setIOSValue: function(v)
17014     {
17015         this.value = v;
17016         
17017         if(!this.ios_options){
17018             return;
17019         }
17020         
17021         Roo.each(this.ios_options, function(opts){
17022            
17023            opts.el.dom.removeAttribute('selected');
17024            
17025            if(opts.data[this.valueField] != v){
17026                return;
17027            }
17028            
17029            opts.el.dom.setAttribute('selected', true);
17030            
17031         }, this);
17032     }
17033
17034     /** 
17035     * @cfg {Boolean} grow 
17036     * @hide 
17037     */
17038     /** 
17039     * @cfg {Number} growMin 
17040     * @hide 
17041     */
17042     /** 
17043     * @cfg {Number} growMax 
17044     * @hide 
17045     */
17046     /**
17047      * @hide
17048      * @method autoSize
17049      */
17050 });
17051
17052 Roo.apply(Roo.bootstrap.ComboBox,  {
17053     
17054     header : {
17055         tag: 'div',
17056         cls: 'modal-header',
17057         cn: [
17058             {
17059                 tag: 'h4',
17060                 cls: 'modal-title'
17061             }
17062         ]
17063     },
17064     
17065     body : {
17066         tag: 'div',
17067         cls: 'modal-body',
17068         cn: [
17069             {
17070                 tag: 'ul',
17071                 cls: 'list-group'
17072             }
17073         ]
17074     },
17075     
17076     listItemRadio : {
17077         tag: 'li',
17078         cls: 'list-group-item',
17079         cn: [
17080             {
17081                 tag: 'span',
17082                 cls: 'roo-combobox-list-group-item-value'
17083             },
17084             {
17085                 tag: 'div',
17086                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17087                 cn: [
17088                     {
17089                         tag: 'input',
17090                         type: 'radio'
17091                     },
17092                     {
17093                         tag: 'label'
17094                     }
17095                 ]
17096             }
17097         ]
17098     },
17099     
17100     listItemCheckbox : {
17101         tag: 'li',
17102         cls: 'list-group-item',
17103         cn: [
17104             {
17105                 tag: 'span',
17106                 cls: 'roo-combobox-list-group-item-value'
17107             },
17108             {
17109                 tag: 'div',
17110                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17111                 cn: [
17112                     {
17113                         tag: 'input',
17114                         type: 'checkbox'
17115                     },
17116                     {
17117                         tag: 'label'
17118                     }
17119                 ]
17120             }
17121         ]
17122     },
17123     
17124     emptyResult : {
17125         tag: 'div',
17126         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17127     },
17128     
17129     footer : {
17130         tag: 'div',
17131         cls: 'modal-footer',
17132         cn: [
17133             {
17134                 tag: 'div',
17135                 cls: 'row',
17136                 cn: [
17137                     {
17138                         tag: 'div',
17139                         cls: 'col-xs-6 text-left',
17140                         cn: {
17141                             tag: 'button',
17142                             cls: 'btn btn-danger roo-touch-view-cancel',
17143                             html: 'Cancel'
17144                         }
17145                     },
17146                     {
17147                         tag: 'div',
17148                         cls: 'col-xs-6 text-right',
17149                         cn: {
17150                             tag: 'button',
17151                             cls: 'btn btn-success roo-touch-view-ok',
17152                             html: 'OK'
17153                         }
17154                     }
17155                 ]
17156             }
17157         ]
17158         
17159     }
17160 });
17161
17162 Roo.apply(Roo.bootstrap.ComboBox,  {
17163     
17164     touchViewTemplate : {
17165         tag: 'div',
17166         cls: 'modal fade roo-combobox-touch-view',
17167         cn: [
17168             {
17169                 tag: 'div',
17170                 cls: 'modal-dialog',
17171                 style : 'position:fixed', // we have to fix position....
17172                 cn: [
17173                     {
17174                         tag: 'div',
17175                         cls: 'modal-content',
17176                         cn: [
17177                             Roo.bootstrap.ComboBox.header,
17178                             Roo.bootstrap.ComboBox.body,
17179                             Roo.bootstrap.ComboBox.footer
17180                         ]
17181                     }
17182                 ]
17183             }
17184         ]
17185     }
17186 });/*
17187  * Based on:
17188  * Ext JS Library 1.1.1
17189  * Copyright(c) 2006-2007, Ext JS, LLC.
17190  *
17191  * Originally Released Under LGPL - original licence link has changed is not relivant.
17192  *
17193  * Fork - LGPL
17194  * <script type="text/javascript">
17195  */
17196
17197 /**
17198  * @class Roo.View
17199  * @extends Roo.util.Observable
17200  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17201  * This class also supports single and multi selection modes. <br>
17202  * Create a data model bound view:
17203  <pre><code>
17204  var store = new Roo.data.Store(...);
17205
17206  var view = new Roo.View({
17207     el : "my-element",
17208     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17209  
17210     singleSelect: true,
17211     selectedClass: "ydataview-selected",
17212     store: store
17213  });
17214
17215  // listen for node click?
17216  view.on("click", function(vw, index, node, e){
17217  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17218  });
17219
17220  // load XML data
17221  dataModel.load("foobar.xml");
17222  </code></pre>
17223  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17224  * <br><br>
17225  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17226  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17227  * 
17228  * Note: old style constructor is still suported (container, template, config)
17229  * 
17230  * @constructor
17231  * Create a new View
17232  * @param {Object} config The config object
17233  * 
17234  */
17235 Roo.View = function(config, depreciated_tpl, depreciated_config){
17236     
17237     this.parent = false;
17238     
17239     if (typeof(depreciated_tpl) == 'undefined') {
17240         // new way.. - universal constructor.
17241         Roo.apply(this, config);
17242         this.el  = Roo.get(this.el);
17243     } else {
17244         // old format..
17245         this.el  = Roo.get(config);
17246         this.tpl = depreciated_tpl;
17247         Roo.apply(this, depreciated_config);
17248     }
17249     this.wrapEl  = this.el.wrap().wrap();
17250     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17251     
17252     
17253     if(typeof(this.tpl) == "string"){
17254         this.tpl = new Roo.Template(this.tpl);
17255     } else {
17256         // support xtype ctors..
17257         this.tpl = new Roo.factory(this.tpl, Roo);
17258     }
17259     
17260     
17261     this.tpl.compile();
17262     
17263     /** @private */
17264     this.addEvents({
17265         /**
17266          * @event beforeclick
17267          * Fires before a click is processed. Returns false to cancel the default action.
17268          * @param {Roo.View} this
17269          * @param {Number} index The index of the target node
17270          * @param {HTMLElement} node The target node
17271          * @param {Roo.EventObject} e The raw event object
17272          */
17273             "beforeclick" : true,
17274         /**
17275          * @event click
17276          * Fires when a template node is clicked.
17277          * @param {Roo.View} this
17278          * @param {Number} index The index of the target node
17279          * @param {HTMLElement} node The target node
17280          * @param {Roo.EventObject} e The raw event object
17281          */
17282             "click" : true,
17283         /**
17284          * @event dblclick
17285          * Fires when a template node is double clicked.
17286          * @param {Roo.View} this
17287          * @param {Number} index The index of the target node
17288          * @param {HTMLElement} node The target node
17289          * @param {Roo.EventObject} e The raw event object
17290          */
17291             "dblclick" : true,
17292         /**
17293          * @event contextmenu
17294          * Fires when a template node is right clicked.
17295          * @param {Roo.View} this
17296          * @param {Number} index The index of the target node
17297          * @param {HTMLElement} node The target node
17298          * @param {Roo.EventObject} e The raw event object
17299          */
17300             "contextmenu" : true,
17301         /**
17302          * @event selectionchange
17303          * Fires when the selected nodes change.
17304          * @param {Roo.View} this
17305          * @param {Array} selections Array of the selected nodes
17306          */
17307             "selectionchange" : true,
17308     
17309         /**
17310          * @event beforeselect
17311          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17312          * @param {Roo.View} this
17313          * @param {HTMLElement} node The node to be selected
17314          * @param {Array} selections Array of currently selected nodes
17315          */
17316             "beforeselect" : true,
17317         /**
17318          * @event preparedata
17319          * Fires on every row to render, to allow you to change the data.
17320          * @param {Roo.View} this
17321          * @param {Object} data to be rendered (change this)
17322          */
17323           "preparedata" : true
17324           
17325           
17326         });
17327
17328
17329
17330     this.el.on({
17331         "click": this.onClick,
17332         "dblclick": this.onDblClick,
17333         "contextmenu": this.onContextMenu,
17334         scope:this
17335     });
17336
17337     this.selections = [];
17338     this.nodes = [];
17339     this.cmp = new Roo.CompositeElementLite([]);
17340     if(this.store){
17341         this.store = Roo.factory(this.store, Roo.data);
17342         this.setStore(this.store, true);
17343     }
17344     
17345     if ( this.footer && this.footer.xtype) {
17346            
17347          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17348         
17349         this.footer.dataSource = this.store;
17350         this.footer.container = fctr;
17351         this.footer = Roo.factory(this.footer, Roo);
17352         fctr.insertFirst(this.el);
17353         
17354         // this is a bit insane - as the paging toolbar seems to detach the el..
17355 //        dom.parentNode.parentNode.parentNode
17356          // they get detached?
17357     }
17358     
17359     
17360     Roo.View.superclass.constructor.call(this);
17361     
17362     
17363 };
17364
17365 Roo.extend(Roo.View, Roo.util.Observable, {
17366     
17367      /**
17368      * @cfg {Roo.data.Store} store Data store to load data from.
17369      */
17370     store : false,
17371     
17372     /**
17373      * @cfg {String|Roo.Element} el The container element.
17374      */
17375     el : '',
17376     
17377     /**
17378      * @cfg {String|Roo.Template} tpl The template used by this View 
17379      */
17380     tpl : false,
17381     /**
17382      * @cfg {String} dataName the named area of the template to use as the data area
17383      *                          Works with domtemplates roo-name="name"
17384      */
17385     dataName: false,
17386     /**
17387      * @cfg {String} selectedClass The css class to add to selected nodes
17388      */
17389     selectedClass : "x-view-selected",
17390      /**
17391      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17392      */
17393     emptyText : "",
17394     
17395     /**
17396      * @cfg {String} text to display on mask (default Loading)
17397      */
17398     mask : false,
17399     /**
17400      * @cfg {Boolean} multiSelect Allow multiple selection
17401      */
17402     multiSelect : false,
17403     /**
17404      * @cfg {Boolean} singleSelect Allow single selection
17405      */
17406     singleSelect:  false,
17407     
17408     /**
17409      * @cfg {Boolean} toggleSelect - selecting 
17410      */
17411     toggleSelect : false,
17412     
17413     /**
17414      * @cfg {Boolean} tickable - selecting 
17415      */
17416     tickable : false,
17417     
17418     /**
17419      * Returns the element this view is bound to.
17420      * @return {Roo.Element}
17421      */
17422     getEl : function(){
17423         return this.wrapEl;
17424     },
17425     
17426     
17427
17428     /**
17429      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17430      */
17431     refresh : function(){
17432         //Roo.log('refresh');
17433         var t = this.tpl;
17434         
17435         // if we are using something like 'domtemplate', then
17436         // the what gets used is:
17437         // t.applySubtemplate(NAME, data, wrapping data..)
17438         // the outer template then get' applied with
17439         //     the store 'extra data'
17440         // and the body get's added to the
17441         //      roo-name="data" node?
17442         //      <span class='roo-tpl-{name}'></span> ?????
17443         
17444         
17445         
17446         this.clearSelections();
17447         this.el.update("");
17448         var html = [];
17449         var records = this.store.getRange();
17450         if(records.length < 1) {
17451             
17452             // is this valid??  = should it render a template??
17453             
17454             this.el.update(this.emptyText);
17455             return;
17456         }
17457         var el = this.el;
17458         if (this.dataName) {
17459             this.el.update(t.apply(this.store.meta)); //????
17460             el = this.el.child('.roo-tpl-' + this.dataName);
17461         }
17462         
17463         for(var i = 0, len = records.length; i < len; i++){
17464             var data = this.prepareData(records[i].data, i, records[i]);
17465             this.fireEvent("preparedata", this, data, i, records[i]);
17466             
17467             var d = Roo.apply({}, data);
17468             
17469             if(this.tickable){
17470                 Roo.apply(d, {'roo-id' : Roo.id()});
17471                 
17472                 var _this = this;
17473             
17474                 Roo.each(this.parent.item, function(item){
17475                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17476                         return;
17477                     }
17478                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17479                 });
17480             }
17481             
17482             html[html.length] = Roo.util.Format.trim(
17483                 this.dataName ?
17484                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17485                     t.apply(d)
17486             );
17487         }
17488         
17489         
17490         
17491         el.update(html.join(""));
17492         this.nodes = el.dom.childNodes;
17493         this.updateIndexes(0);
17494     },
17495     
17496
17497     /**
17498      * Function to override to reformat the data that is sent to
17499      * the template for each node.
17500      * DEPRICATED - use the preparedata event handler.
17501      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17502      * a JSON object for an UpdateManager bound view).
17503      */
17504     prepareData : function(data, index, record)
17505     {
17506         this.fireEvent("preparedata", this, data, index, record);
17507         return data;
17508     },
17509
17510     onUpdate : function(ds, record){
17511         // Roo.log('on update');   
17512         this.clearSelections();
17513         var index = this.store.indexOf(record);
17514         var n = this.nodes[index];
17515         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17516         n.parentNode.removeChild(n);
17517         this.updateIndexes(index, index);
17518     },
17519
17520     
17521     
17522 // --------- FIXME     
17523     onAdd : function(ds, records, index)
17524     {
17525         //Roo.log(['on Add', ds, records, index] );        
17526         this.clearSelections();
17527         if(this.nodes.length == 0){
17528             this.refresh();
17529             return;
17530         }
17531         var n = this.nodes[index];
17532         for(var i = 0, len = records.length; i < len; i++){
17533             var d = this.prepareData(records[i].data, i, records[i]);
17534             if(n){
17535                 this.tpl.insertBefore(n, d);
17536             }else{
17537                 
17538                 this.tpl.append(this.el, d);
17539             }
17540         }
17541         this.updateIndexes(index);
17542     },
17543
17544     onRemove : function(ds, record, index){
17545        // Roo.log('onRemove');
17546         this.clearSelections();
17547         var el = this.dataName  ?
17548             this.el.child('.roo-tpl-' + this.dataName) :
17549             this.el; 
17550         
17551         el.dom.removeChild(this.nodes[index]);
17552         this.updateIndexes(index);
17553     },
17554
17555     /**
17556      * Refresh an individual node.
17557      * @param {Number} index
17558      */
17559     refreshNode : function(index){
17560         this.onUpdate(this.store, this.store.getAt(index));
17561     },
17562
17563     updateIndexes : function(startIndex, endIndex){
17564         var ns = this.nodes;
17565         startIndex = startIndex || 0;
17566         endIndex = endIndex || ns.length - 1;
17567         for(var i = startIndex; i <= endIndex; i++){
17568             ns[i].nodeIndex = i;
17569         }
17570     },
17571
17572     /**
17573      * Changes the data store this view uses and refresh the view.
17574      * @param {Store} store
17575      */
17576     setStore : function(store, initial){
17577         if(!initial && this.store){
17578             this.store.un("datachanged", this.refresh);
17579             this.store.un("add", this.onAdd);
17580             this.store.un("remove", this.onRemove);
17581             this.store.un("update", this.onUpdate);
17582             this.store.un("clear", this.refresh);
17583             this.store.un("beforeload", this.onBeforeLoad);
17584             this.store.un("load", this.onLoad);
17585             this.store.un("loadexception", this.onLoad);
17586         }
17587         if(store){
17588           
17589             store.on("datachanged", this.refresh, this);
17590             store.on("add", this.onAdd, this);
17591             store.on("remove", this.onRemove, this);
17592             store.on("update", this.onUpdate, this);
17593             store.on("clear", this.refresh, this);
17594             store.on("beforeload", this.onBeforeLoad, this);
17595             store.on("load", this.onLoad, this);
17596             store.on("loadexception", this.onLoad, this);
17597         }
17598         
17599         if(store){
17600             this.refresh();
17601         }
17602     },
17603     /**
17604      * onbeforeLoad - masks the loading area.
17605      *
17606      */
17607     onBeforeLoad : function(store,opts)
17608     {
17609          //Roo.log('onBeforeLoad');   
17610         if (!opts.add) {
17611             this.el.update("");
17612         }
17613         this.el.mask(this.mask ? this.mask : "Loading" ); 
17614     },
17615     onLoad : function ()
17616     {
17617         this.el.unmask();
17618     },
17619     
17620
17621     /**
17622      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17623      * @param {HTMLElement} node
17624      * @return {HTMLElement} The template node
17625      */
17626     findItemFromChild : function(node){
17627         var el = this.dataName  ?
17628             this.el.child('.roo-tpl-' + this.dataName,true) :
17629             this.el.dom; 
17630         
17631         if(!node || node.parentNode == el){
17632                     return node;
17633             }
17634             var p = node.parentNode;
17635             while(p && p != el){
17636             if(p.parentNode == el){
17637                 return p;
17638             }
17639             p = p.parentNode;
17640         }
17641             return null;
17642     },
17643
17644     /** @ignore */
17645     onClick : function(e){
17646         var item = this.findItemFromChild(e.getTarget());
17647         if(item){
17648             var index = this.indexOf(item);
17649             if(this.onItemClick(item, index, e) !== false){
17650                 this.fireEvent("click", this, index, item, e);
17651             }
17652         }else{
17653             this.clearSelections();
17654         }
17655     },
17656
17657     /** @ignore */
17658     onContextMenu : function(e){
17659         var item = this.findItemFromChild(e.getTarget());
17660         if(item){
17661             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17662         }
17663     },
17664
17665     /** @ignore */
17666     onDblClick : function(e){
17667         var item = this.findItemFromChild(e.getTarget());
17668         if(item){
17669             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17670         }
17671     },
17672
17673     onItemClick : function(item, index, e)
17674     {
17675         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17676             return false;
17677         }
17678         if (this.toggleSelect) {
17679             var m = this.isSelected(item) ? 'unselect' : 'select';
17680             //Roo.log(m);
17681             var _t = this;
17682             _t[m](item, true, false);
17683             return true;
17684         }
17685         if(this.multiSelect || this.singleSelect){
17686             if(this.multiSelect && e.shiftKey && this.lastSelection){
17687                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17688             }else{
17689                 this.select(item, this.multiSelect && e.ctrlKey);
17690                 this.lastSelection = item;
17691             }
17692             
17693             if(!this.tickable){
17694                 e.preventDefault();
17695             }
17696             
17697         }
17698         return true;
17699     },
17700
17701     /**
17702      * Get the number of selected nodes.
17703      * @return {Number}
17704      */
17705     getSelectionCount : function(){
17706         return this.selections.length;
17707     },
17708
17709     /**
17710      * Get the currently selected nodes.
17711      * @return {Array} An array of HTMLElements
17712      */
17713     getSelectedNodes : function(){
17714         return this.selections;
17715     },
17716
17717     /**
17718      * Get the indexes of the selected nodes.
17719      * @return {Array}
17720      */
17721     getSelectedIndexes : function(){
17722         var indexes = [], s = this.selections;
17723         for(var i = 0, len = s.length; i < len; i++){
17724             indexes.push(s[i].nodeIndex);
17725         }
17726         return indexes;
17727     },
17728
17729     /**
17730      * Clear all selections
17731      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17732      */
17733     clearSelections : function(suppressEvent){
17734         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17735             this.cmp.elements = this.selections;
17736             this.cmp.removeClass(this.selectedClass);
17737             this.selections = [];
17738             if(!suppressEvent){
17739                 this.fireEvent("selectionchange", this, this.selections);
17740             }
17741         }
17742     },
17743
17744     /**
17745      * Returns true if the passed node is selected
17746      * @param {HTMLElement/Number} node The node or node index
17747      * @return {Boolean}
17748      */
17749     isSelected : function(node){
17750         var s = this.selections;
17751         if(s.length < 1){
17752             return false;
17753         }
17754         node = this.getNode(node);
17755         return s.indexOf(node) !== -1;
17756     },
17757
17758     /**
17759      * Selects nodes.
17760      * @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
17761      * @param {Boolean} keepExisting (optional) true to keep existing selections
17762      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17763      */
17764     select : function(nodeInfo, keepExisting, suppressEvent){
17765         if(nodeInfo instanceof Array){
17766             if(!keepExisting){
17767                 this.clearSelections(true);
17768             }
17769             for(var i = 0, len = nodeInfo.length; i < len; i++){
17770                 this.select(nodeInfo[i], true, true);
17771             }
17772             return;
17773         } 
17774         var node = this.getNode(nodeInfo);
17775         if(!node || this.isSelected(node)){
17776             return; // already selected.
17777         }
17778         if(!keepExisting){
17779             this.clearSelections(true);
17780         }
17781         
17782         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17783             Roo.fly(node).addClass(this.selectedClass);
17784             this.selections.push(node);
17785             if(!suppressEvent){
17786                 this.fireEvent("selectionchange", this, this.selections);
17787             }
17788         }
17789         
17790         
17791     },
17792       /**
17793      * Unselects nodes.
17794      * @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
17795      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17796      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17797      */
17798     unselect : function(nodeInfo, keepExisting, suppressEvent)
17799     {
17800         if(nodeInfo instanceof Array){
17801             Roo.each(this.selections, function(s) {
17802                 this.unselect(s, nodeInfo);
17803             }, this);
17804             return;
17805         }
17806         var node = this.getNode(nodeInfo);
17807         if(!node || !this.isSelected(node)){
17808             //Roo.log("not selected");
17809             return; // not selected.
17810         }
17811         // fireevent???
17812         var ns = [];
17813         Roo.each(this.selections, function(s) {
17814             if (s == node ) {
17815                 Roo.fly(node).removeClass(this.selectedClass);
17816
17817                 return;
17818             }
17819             ns.push(s);
17820         },this);
17821         
17822         this.selections= ns;
17823         this.fireEvent("selectionchange", this, this.selections);
17824     },
17825
17826     /**
17827      * Gets a template node.
17828      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17829      * @return {HTMLElement} The node or null if it wasn't found
17830      */
17831     getNode : function(nodeInfo){
17832         if(typeof nodeInfo == "string"){
17833             return document.getElementById(nodeInfo);
17834         }else if(typeof nodeInfo == "number"){
17835             return this.nodes[nodeInfo];
17836         }
17837         return nodeInfo;
17838     },
17839
17840     /**
17841      * Gets a range template nodes.
17842      * @param {Number} startIndex
17843      * @param {Number} endIndex
17844      * @return {Array} An array of nodes
17845      */
17846     getNodes : function(start, end){
17847         var ns = this.nodes;
17848         start = start || 0;
17849         end = typeof end == "undefined" ? ns.length - 1 : end;
17850         var nodes = [];
17851         if(start <= end){
17852             for(var i = start; i <= end; i++){
17853                 nodes.push(ns[i]);
17854             }
17855         } else{
17856             for(var i = start; i >= end; i--){
17857                 nodes.push(ns[i]);
17858             }
17859         }
17860         return nodes;
17861     },
17862
17863     /**
17864      * Finds the index of the passed node
17865      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17866      * @return {Number} The index of the node or -1
17867      */
17868     indexOf : function(node){
17869         node = this.getNode(node);
17870         if(typeof node.nodeIndex == "number"){
17871             return node.nodeIndex;
17872         }
17873         var ns = this.nodes;
17874         for(var i = 0, len = ns.length; i < len; i++){
17875             if(ns[i] == node){
17876                 return i;
17877             }
17878         }
17879         return -1;
17880     }
17881 });
17882 /*
17883  * - LGPL
17884  *
17885  * based on jquery fullcalendar
17886  * 
17887  */
17888
17889 Roo.bootstrap = Roo.bootstrap || {};
17890 /**
17891  * @class Roo.bootstrap.Calendar
17892  * @extends Roo.bootstrap.Component
17893  * Bootstrap Calendar class
17894  * @cfg {Boolean} loadMask (true|false) default false
17895  * @cfg {Object} header generate the user specific header of the calendar, default false
17896
17897  * @constructor
17898  * Create a new Container
17899  * @param {Object} config The config object
17900  */
17901
17902
17903
17904 Roo.bootstrap.Calendar = function(config){
17905     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17906      this.addEvents({
17907         /**
17908              * @event select
17909              * Fires when a date is selected
17910              * @param {DatePicker} this
17911              * @param {Date} date The selected date
17912              */
17913         'select': true,
17914         /**
17915              * @event monthchange
17916              * Fires when the displayed month changes 
17917              * @param {DatePicker} this
17918              * @param {Date} date The selected month
17919              */
17920         'monthchange': true,
17921         /**
17922              * @event evententer
17923              * Fires when mouse over an event
17924              * @param {Calendar} this
17925              * @param {event} Event
17926              */
17927         'evententer': true,
17928         /**
17929              * @event eventleave
17930              * Fires when the mouse leaves an
17931              * @param {Calendar} this
17932              * @param {event}
17933              */
17934         'eventleave': true,
17935         /**
17936              * @event eventclick
17937              * Fires when the mouse click an
17938              * @param {Calendar} this
17939              * @param {event}
17940              */
17941         'eventclick': true
17942         
17943     });
17944
17945 };
17946
17947 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17948     
17949      /**
17950      * @cfg {Number} startDay
17951      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17952      */
17953     startDay : 0,
17954     
17955     loadMask : false,
17956     
17957     header : false,
17958       
17959     getAutoCreate : function(){
17960         
17961         
17962         var fc_button = function(name, corner, style, content ) {
17963             return Roo.apply({},{
17964                 tag : 'span',
17965                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17966                          (corner.length ?
17967                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17968                             ''
17969                         ),
17970                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17971                 unselectable: 'on'
17972             });
17973         };
17974         
17975         var header = {};
17976         
17977         if(!this.header){
17978             header = {
17979                 tag : 'table',
17980                 cls : 'fc-header',
17981                 style : 'width:100%',
17982                 cn : [
17983                     {
17984                         tag: 'tr',
17985                         cn : [
17986                             {
17987                                 tag : 'td',
17988                                 cls : 'fc-header-left',
17989                                 cn : [
17990                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17991                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17992                                     { tag: 'span', cls: 'fc-header-space' },
17993                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17994
17995
17996                                 ]
17997                             },
17998
17999                             {
18000                                 tag : 'td',
18001                                 cls : 'fc-header-center',
18002                                 cn : [
18003                                     {
18004                                         tag: 'span',
18005                                         cls: 'fc-header-title',
18006                                         cn : {
18007                                             tag: 'H2',
18008                                             html : 'month / year'
18009                                         }
18010                                     }
18011
18012                                 ]
18013                             },
18014                             {
18015                                 tag : 'td',
18016                                 cls : 'fc-header-right',
18017                                 cn : [
18018                               /*      fc_button('month', 'left', '', 'month' ),
18019                                     fc_button('week', '', '', 'week' ),
18020                                     fc_button('day', 'right', '', 'day' )
18021                                 */    
18022
18023                                 ]
18024                             }
18025
18026                         ]
18027                     }
18028                 ]
18029             };
18030         }
18031         
18032         header = this.header;
18033         
18034        
18035         var cal_heads = function() {
18036             var ret = [];
18037             // fixme - handle this.
18038             
18039             for (var i =0; i < Date.dayNames.length; i++) {
18040                 var d = Date.dayNames[i];
18041                 ret.push({
18042                     tag: 'th',
18043                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18044                     html : d.substring(0,3)
18045                 });
18046                 
18047             }
18048             ret[0].cls += ' fc-first';
18049             ret[6].cls += ' fc-last';
18050             return ret;
18051         };
18052         var cal_cell = function(n) {
18053             return  {
18054                 tag: 'td',
18055                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18056                 cn : [
18057                     {
18058                         cn : [
18059                             {
18060                                 cls: 'fc-day-number',
18061                                 html: 'D'
18062                             },
18063                             {
18064                                 cls: 'fc-day-content',
18065                              
18066                                 cn : [
18067                                      {
18068                                         style: 'position: relative;' // height: 17px;
18069                                     }
18070                                 ]
18071                             }
18072                             
18073                             
18074                         ]
18075                     }
18076                 ]
18077                 
18078             }
18079         };
18080         var cal_rows = function() {
18081             
18082             var ret = [];
18083             for (var r = 0; r < 6; r++) {
18084                 var row= {
18085                     tag : 'tr',
18086                     cls : 'fc-week',
18087                     cn : []
18088                 };
18089                 
18090                 for (var i =0; i < Date.dayNames.length; i++) {
18091                     var d = Date.dayNames[i];
18092                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18093
18094                 }
18095                 row.cn[0].cls+=' fc-first';
18096                 row.cn[0].cn[0].style = 'min-height:90px';
18097                 row.cn[6].cls+=' fc-last';
18098                 ret.push(row);
18099                 
18100             }
18101             ret[0].cls += ' fc-first';
18102             ret[4].cls += ' fc-prev-last';
18103             ret[5].cls += ' fc-last';
18104             return ret;
18105             
18106         };
18107         
18108         var cal_table = {
18109             tag: 'table',
18110             cls: 'fc-border-separate',
18111             style : 'width:100%',
18112             cellspacing  : 0,
18113             cn : [
18114                 { 
18115                     tag: 'thead',
18116                     cn : [
18117                         { 
18118                             tag: 'tr',
18119                             cls : 'fc-first fc-last',
18120                             cn : cal_heads()
18121                         }
18122                     ]
18123                 },
18124                 { 
18125                     tag: 'tbody',
18126                     cn : cal_rows()
18127                 }
18128                   
18129             ]
18130         };
18131          
18132          var cfg = {
18133             cls : 'fc fc-ltr',
18134             cn : [
18135                 header,
18136                 {
18137                     cls : 'fc-content',
18138                     style : "position: relative;",
18139                     cn : [
18140                         {
18141                             cls : 'fc-view fc-view-month fc-grid',
18142                             style : 'position: relative',
18143                             unselectable : 'on',
18144                             cn : [
18145                                 {
18146                                     cls : 'fc-event-container',
18147                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18148                                 },
18149                                 cal_table
18150                             ]
18151                         }
18152                     ]
18153     
18154                 }
18155            ] 
18156             
18157         };
18158         
18159          
18160         
18161         return cfg;
18162     },
18163     
18164     
18165     initEvents : function()
18166     {
18167         if(!this.store){
18168             throw "can not find store for calendar";
18169         }
18170         
18171         var mark = {
18172             tag: "div",
18173             cls:"x-dlg-mask",
18174             style: "text-align:center",
18175             cn: [
18176                 {
18177                     tag: "div",
18178                     style: "background-color:white;width:50%;margin:250 auto",
18179                     cn: [
18180                         {
18181                             tag: "img",
18182                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18183                         },
18184                         {
18185                             tag: "span",
18186                             html: "Loading"
18187                         }
18188                         
18189                     ]
18190                 }
18191             ]
18192         };
18193         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18194         
18195         var size = this.el.select('.fc-content', true).first().getSize();
18196         this.maskEl.setSize(size.width, size.height);
18197         this.maskEl.enableDisplayMode("block");
18198         if(!this.loadMask){
18199             this.maskEl.hide();
18200         }
18201         
18202         this.store = Roo.factory(this.store, Roo.data);
18203         this.store.on('load', this.onLoad, this);
18204         this.store.on('beforeload', this.onBeforeLoad, this);
18205         
18206         this.resize();
18207         
18208         this.cells = this.el.select('.fc-day',true);
18209         //Roo.log(this.cells);
18210         this.textNodes = this.el.query('.fc-day-number');
18211         this.cells.addClassOnOver('fc-state-hover');
18212         
18213         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18214         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18215         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18216         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18217         
18218         this.on('monthchange', this.onMonthChange, this);
18219         
18220         this.update(new Date().clearTime());
18221     },
18222     
18223     resize : function() {
18224         var sz  = this.el.getSize();
18225         
18226         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18227         this.el.select('.fc-day-content div',true).setHeight(34);
18228     },
18229     
18230     
18231     // private
18232     showPrevMonth : function(e){
18233         this.update(this.activeDate.add("mo", -1));
18234     },
18235     showToday : function(e){
18236         this.update(new Date().clearTime());
18237     },
18238     // private
18239     showNextMonth : function(e){
18240         this.update(this.activeDate.add("mo", 1));
18241     },
18242
18243     // private
18244     showPrevYear : function(){
18245         this.update(this.activeDate.add("y", -1));
18246     },
18247
18248     // private
18249     showNextYear : function(){
18250         this.update(this.activeDate.add("y", 1));
18251     },
18252
18253     
18254    // private
18255     update : function(date)
18256     {
18257         var vd = this.activeDate;
18258         this.activeDate = date;
18259 //        if(vd && this.el){
18260 //            var t = date.getTime();
18261 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18262 //                Roo.log('using add remove');
18263 //                
18264 //                this.fireEvent('monthchange', this, date);
18265 //                
18266 //                this.cells.removeClass("fc-state-highlight");
18267 //                this.cells.each(function(c){
18268 //                   if(c.dateValue == t){
18269 //                       c.addClass("fc-state-highlight");
18270 //                       setTimeout(function(){
18271 //                            try{c.dom.firstChild.focus();}catch(e){}
18272 //                       }, 50);
18273 //                       return false;
18274 //                   }
18275 //                   return true;
18276 //                });
18277 //                return;
18278 //            }
18279 //        }
18280         
18281         var days = date.getDaysInMonth();
18282         
18283         var firstOfMonth = date.getFirstDateOfMonth();
18284         var startingPos = firstOfMonth.getDay()-this.startDay;
18285         
18286         if(startingPos < this.startDay){
18287             startingPos += 7;
18288         }
18289         
18290         var pm = date.add(Date.MONTH, -1);
18291         var prevStart = pm.getDaysInMonth()-startingPos;
18292 //        
18293         this.cells = this.el.select('.fc-day',true);
18294         this.textNodes = this.el.query('.fc-day-number');
18295         this.cells.addClassOnOver('fc-state-hover');
18296         
18297         var cells = this.cells.elements;
18298         var textEls = this.textNodes;
18299         
18300         Roo.each(cells, function(cell){
18301             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18302         });
18303         
18304         days += startingPos;
18305
18306         // convert everything to numbers so it's fast
18307         var day = 86400000;
18308         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18309         //Roo.log(d);
18310         //Roo.log(pm);
18311         //Roo.log(prevStart);
18312         
18313         var today = new Date().clearTime().getTime();
18314         var sel = date.clearTime().getTime();
18315         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18316         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18317         var ddMatch = this.disabledDatesRE;
18318         var ddText = this.disabledDatesText;
18319         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18320         var ddaysText = this.disabledDaysText;
18321         var format = this.format;
18322         
18323         var setCellClass = function(cal, cell){
18324             cell.row = 0;
18325             cell.events = [];
18326             cell.more = [];
18327             //Roo.log('set Cell Class');
18328             cell.title = "";
18329             var t = d.getTime();
18330             
18331             //Roo.log(d);
18332             
18333             cell.dateValue = t;
18334             if(t == today){
18335                 cell.className += " fc-today";
18336                 cell.className += " fc-state-highlight";
18337                 cell.title = cal.todayText;
18338             }
18339             if(t == sel){
18340                 // disable highlight in other month..
18341                 //cell.className += " fc-state-highlight";
18342                 
18343             }
18344             // disabling
18345             if(t < min) {
18346                 cell.className = " fc-state-disabled";
18347                 cell.title = cal.minText;
18348                 return;
18349             }
18350             if(t > max) {
18351                 cell.className = " fc-state-disabled";
18352                 cell.title = cal.maxText;
18353                 return;
18354             }
18355             if(ddays){
18356                 if(ddays.indexOf(d.getDay()) != -1){
18357                     cell.title = ddaysText;
18358                     cell.className = " fc-state-disabled";
18359                 }
18360             }
18361             if(ddMatch && format){
18362                 var fvalue = d.dateFormat(format);
18363                 if(ddMatch.test(fvalue)){
18364                     cell.title = ddText.replace("%0", fvalue);
18365                     cell.className = " fc-state-disabled";
18366                 }
18367             }
18368             
18369             if (!cell.initialClassName) {
18370                 cell.initialClassName = cell.dom.className;
18371             }
18372             
18373             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18374         };
18375
18376         var i = 0;
18377         
18378         for(; i < startingPos; i++) {
18379             textEls[i].innerHTML = (++prevStart);
18380             d.setDate(d.getDate()+1);
18381             
18382             cells[i].className = "fc-past fc-other-month";
18383             setCellClass(this, cells[i]);
18384         }
18385         
18386         var intDay = 0;
18387         
18388         for(; i < days; i++){
18389             intDay = i - startingPos + 1;
18390             textEls[i].innerHTML = (intDay);
18391             d.setDate(d.getDate()+1);
18392             
18393             cells[i].className = ''; // "x-date-active";
18394             setCellClass(this, cells[i]);
18395         }
18396         var extraDays = 0;
18397         
18398         for(; i < 42; i++) {
18399             textEls[i].innerHTML = (++extraDays);
18400             d.setDate(d.getDate()+1);
18401             
18402             cells[i].className = "fc-future fc-other-month";
18403             setCellClass(this, cells[i]);
18404         }
18405         
18406         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18407         
18408         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18409         
18410         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18411         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18412         
18413         if(totalRows != 6){
18414             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18415             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18416         }
18417         
18418         this.fireEvent('monthchange', this, date);
18419         
18420         
18421         /*
18422         if(!this.internalRender){
18423             var main = this.el.dom.firstChild;
18424             var w = main.offsetWidth;
18425             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18426             Roo.fly(main).setWidth(w);
18427             this.internalRender = true;
18428             // opera does not respect the auto grow header center column
18429             // then, after it gets a width opera refuses to recalculate
18430             // without a second pass
18431             if(Roo.isOpera && !this.secondPass){
18432                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18433                 this.secondPass = true;
18434                 this.update.defer(10, this, [date]);
18435             }
18436         }
18437         */
18438         
18439     },
18440     
18441     findCell : function(dt) {
18442         dt = dt.clearTime().getTime();
18443         var ret = false;
18444         this.cells.each(function(c){
18445             //Roo.log("check " +c.dateValue + '?=' + dt);
18446             if(c.dateValue == dt){
18447                 ret = c;
18448                 return false;
18449             }
18450             return true;
18451         });
18452         
18453         return ret;
18454     },
18455     
18456     findCells : function(ev) {
18457         var s = ev.start.clone().clearTime().getTime();
18458        // Roo.log(s);
18459         var e= ev.end.clone().clearTime().getTime();
18460        // Roo.log(e);
18461         var ret = [];
18462         this.cells.each(function(c){
18463              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18464             
18465             if(c.dateValue > e){
18466                 return ;
18467             }
18468             if(c.dateValue < s){
18469                 return ;
18470             }
18471             ret.push(c);
18472         });
18473         
18474         return ret;    
18475     },
18476     
18477 //    findBestRow: function(cells)
18478 //    {
18479 //        var ret = 0;
18480 //        
18481 //        for (var i =0 ; i < cells.length;i++) {
18482 //            ret  = Math.max(cells[i].rows || 0,ret);
18483 //        }
18484 //        return ret;
18485 //        
18486 //    },
18487     
18488     
18489     addItem : function(ev)
18490     {
18491         // look for vertical location slot in
18492         var cells = this.findCells(ev);
18493         
18494 //        ev.row = this.findBestRow(cells);
18495         
18496         // work out the location.
18497         
18498         var crow = false;
18499         var rows = [];
18500         for(var i =0; i < cells.length; i++) {
18501             
18502             cells[i].row = cells[0].row;
18503             
18504             if(i == 0){
18505                 cells[i].row = cells[i].row + 1;
18506             }
18507             
18508             if (!crow) {
18509                 crow = {
18510                     start : cells[i],
18511                     end :  cells[i]
18512                 };
18513                 continue;
18514             }
18515             if (crow.start.getY() == cells[i].getY()) {
18516                 // on same row.
18517                 crow.end = cells[i];
18518                 continue;
18519             }
18520             // different row.
18521             rows.push(crow);
18522             crow = {
18523                 start: cells[i],
18524                 end : cells[i]
18525             };
18526             
18527         }
18528         
18529         rows.push(crow);
18530         ev.els = [];
18531         ev.rows = rows;
18532         ev.cells = cells;
18533         
18534         cells[0].events.push(ev);
18535         
18536         this.calevents.push(ev);
18537     },
18538     
18539     clearEvents: function() {
18540         
18541         if(!this.calevents){
18542             return;
18543         }
18544         
18545         Roo.each(this.cells.elements, function(c){
18546             c.row = 0;
18547             c.events = [];
18548             c.more = [];
18549         });
18550         
18551         Roo.each(this.calevents, function(e) {
18552             Roo.each(e.els, function(el) {
18553                 el.un('mouseenter' ,this.onEventEnter, this);
18554                 el.un('mouseleave' ,this.onEventLeave, this);
18555                 el.remove();
18556             },this);
18557         },this);
18558         
18559         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18560             e.remove();
18561         });
18562         
18563     },
18564     
18565     renderEvents: function()
18566     {   
18567         var _this = this;
18568         
18569         this.cells.each(function(c) {
18570             
18571             if(c.row < 5){
18572                 return;
18573             }
18574             
18575             var ev = c.events;
18576             
18577             var r = 4;
18578             if(c.row != c.events.length){
18579                 r = 4 - (4 - (c.row - c.events.length));
18580             }
18581             
18582             c.events = ev.slice(0, r);
18583             c.more = ev.slice(r);
18584             
18585             if(c.more.length && c.more.length == 1){
18586                 c.events.push(c.more.pop());
18587             }
18588             
18589             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18590             
18591         });
18592             
18593         this.cells.each(function(c) {
18594             
18595             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18596             
18597             
18598             for (var e = 0; e < c.events.length; e++){
18599                 var ev = c.events[e];
18600                 var rows = ev.rows;
18601                 
18602                 for(var i = 0; i < rows.length; i++) {
18603                 
18604                     // how many rows should it span..
18605
18606                     var  cfg = {
18607                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18608                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18609
18610                         unselectable : "on",
18611                         cn : [
18612                             {
18613                                 cls: 'fc-event-inner',
18614                                 cn : [
18615     //                                {
18616     //                                  tag:'span',
18617     //                                  cls: 'fc-event-time',
18618     //                                  html : cells.length > 1 ? '' : ev.time
18619     //                                },
18620                                     {
18621                                       tag:'span',
18622                                       cls: 'fc-event-title',
18623                                       html : String.format('{0}', ev.title)
18624                                     }
18625
18626
18627                                 ]
18628                             },
18629                             {
18630                                 cls: 'ui-resizable-handle ui-resizable-e',
18631                                 html : '&nbsp;&nbsp;&nbsp'
18632                             }
18633
18634                         ]
18635                     };
18636
18637                     if (i == 0) {
18638                         cfg.cls += ' fc-event-start';
18639                     }
18640                     if ((i+1) == rows.length) {
18641                         cfg.cls += ' fc-event-end';
18642                     }
18643
18644                     var ctr = _this.el.select('.fc-event-container',true).first();
18645                     var cg = ctr.createChild(cfg);
18646
18647                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18648                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18649
18650                     var r = (c.more.length) ? 1 : 0;
18651                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18652                     cg.setWidth(ebox.right - sbox.x -2);
18653
18654                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18655                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18656                     cg.on('click', _this.onEventClick, _this, ev);
18657
18658                     ev.els.push(cg);
18659                     
18660                 }
18661                 
18662             }
18663             
18664             
18665             if(c.more.length){
18666                 var  cfg = {
18667                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18668                     style : 'position: absolute',
18669                     unselectable : "on",
18670                     cn : [
18671                         {
18672                             cls: 'fc-event-inner',
18673                             cn : [
18674                                 {
18675                                   tag:'span',
18676                                   cls: 'fc-event-title',
18677                                   html : 'More'
18678                                 }
18679
18680
18681                             ]
18682                         },
18683                         {
18684                             cls: 'ui-resizable-handle ui-resizable-e',
18685                             html : '&nbsp;&nbsp;&nbsp'
18686                         }
18687
18688                     ]
18689                 };
18690
18691                 var ctr = _this.el.select('.fc-event-container',true).first();
18692                 var cg = ctr.createChild(cfg);
18693
18694                 var sbox = c.select('.fc-day-content',true).first().getBox();
18695                 var ebox = c.select('.fc-day-content',true).first().getBox();
18696                 //Roo.log(cg);
18697                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18698                 cg.setWidth(ebox.right - sbox.x -2);
18699
18700                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18701                 
18702             }
18703             
18704         });
18705         
18706         
18707         
18708     },
18709     
18710     onEventEnter: function (e, el,event,d) {
18711         this.fireEvent('evententer', this, el, event);
18712     },
18713     
18714     onEventLeave: function (e, el,event,d) {
18715         this.fireEvent('eventleave', this, el, event);
18716     },
18717     
18718     onEventClick: function (e, el,event,d) {
18719         this.fireEvent('eventclick', this, el, event);
18720     },
18721     
18722     onMonthChange: function () {
18723         this.store.load();
18724     },
18725     
18726     onMoreEventClick: function(e, el, more)
18727     {
18728         var _this = this;
18729         
18730         this.calpopover.placement = 'right';
18731         this.calpopover.setTitle('More');
18732         
18733         this.calpopover.setContent('');
18734         
18735         var ctr = this.calpopover.el.select('.popover-content', true).first();
18736         
18737         Roo.each(more, function(m){
18738             var cfg = {
18739                 cls : 'fc-event-hori fc-event-draggable',
18740                 html : m.title
18741             };
18742             var cg = ctr.createChild(cfg);
18743             
18744             cg.on('click', _this.onEventClick, _this, m);
18745         });
18746         
18747         this.calpopover.show(el);
18748         
18749         
18750     },
18751     
18752     onLoad: function () 
18753     {   
18754         this.calevents = [];
18755         var cal = this;
18756         
18757         if(this.store.getCount() > 0){
18758             this.store.data.each(function(d){
18759                cal.addItem({
18760                     id : d.data.id,
18761                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18762                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18763                     time : d.data.start_time,
18764                     title : d.data.title,
18765                     description : d.data.description,
18766                     venue : d.data.venue
18767                 });
18768             });
18769         }
18770         
18771         this.renderEvents();
18772         
18773         if(this.calevents.length && this.loadMask){
18774             this.maskEl.hide();
18775         }
18776     },
18777     
18778     onBeforeLoad: function()
18779     {
18780         this.clearEvents();
18781         if(this.loadMask){
18782             this.maskEl.show();
18783         }
18784     }
18785 });
18786
18787  
18788  /*
18789  * - LGPL
18790  *
18791  * element
18792  * 
18793  */
18794
18795 /**
18796  * @class Roo.bootstrap.Popover
18797  * @extends Roo.bootstrap.Component
18798  * Bootstrap Popover class
18799  * @cfg {String} html contents of the popover   (or false to use children..)
18800  * @cfg {String} title of popover (or false to hide)
18801  * @cfg {String} placement how it is placed
18802  * @cfg {String} trigger click || hover (or false to trigger manually)
18803  * @cfg {String} over what (parent or false to trigger manually.)
18804  * @cfg {Number} delay - delay before showing
18805  
18806  * @constructor
18807  * Create a new Popover
18808  * @param {Object} config The config object
18809  */
18810
18811 Roo.bootstrap.Popover = function(config){
18812     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18813     
18814     this.addEvents({
18815         // raw events
18816          /**
18817          * @event show
18818          * After the popover show
18819          * 
18820          * @param {Roo.bootstrap.Popover} this
18821          */
18822         "show" : true,
18823         /**
18824          * @event hide
18825          * After the popover hide
18826          * 
18827          * @param {Roo.bootstrap.Popover} this
18828          */
18829         "hide" : true
18830     });
18831 };
18832
18833 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18834     
18835     title: 'Fill in a title',
18836     html: false,
18837     
18838     placement : 'right',
18839     trigger : 'hover', // hover
18840     
18841     delay : 0,
18842     
18843     over: 'parent',
18844     
18845     can_build_overlaid : false,
18846     
18847     getChildContainer : function()
18848     {
18849         return this.el.select('.popover-content',true).first();
18850     },
18851     
18852     getAutoCreate : function(){
18853          
18854         var cfg = {
18855            cls : 'popover roo-dynamic',
18856            style: 'display:block',
18857            cn : [
18858                 {
18859                     cls : 'arrow'
18860                 },
18861                 {
18862                     cls : 'popover-inner',
18863                     cn : [
18864                         {
18865                             tag: 'h3',
18866                             cls: 'popover-title popover-header',
18867                             html : this.title
18868                         },
18869                         {
18870                             cls : 'popover-content popover-body',
18871                             html : this.html
18872                         }
18873                     ]
18874                     
18875                 }
18876            ]
18877         };
18878         
18879         return cfg;
18880     },
18881     setTitle: function(str)
18882     {
18883         this.title = str;
18884         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18885     },
18886     setContent: function(str)
18887     {
18888         this.html = str;
18889         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18890     },
18891     // as it get's added to the bottom of the page.
18892     onRender : function(ct, position)
18893     {
18894         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18895         if(!this.el){
18896             var cfg = Roo.apply({},  this.getAutoCreate());
18897             cfg.id = Roo.id();
18898             
18899             if (this.cls) {
18900                 cfg.cls += ' ' + this.cls;
18901             }
18902             if (this.style) {
18903                 cfg.style = this.style;
18904             }
18905             //Roo.log("adding to ");
18906             this.el = Roo.get(document.body).createChild(cfg, position);
18907 //            Roo.log(this.el);
18908         }
18909         this.initEvents();
18910     },
18911     
18912     initEvents : function()
18913     {
18914         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18915         this.el.enableDisplayMode('block');
18916         this.el.hide();
18917         if (this.over === false) {
18918             return; 
18919         }
18920         if (this.triggers === false) {
18921             return;
18922         }
18923         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18924         var triggers = this.trigger ? this.trigger.split(' ') : [];
18925         Roo.each(triggers, function(trigger) {
18926         
18927             if (trigger == 'click') {
18928                 on_el.on('click', this.toggle, this);
18929             } else if (trigger != 'manual') {
18930                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18931                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18932       
18933                 on_el.on(eventIn  ,this.enter, this);
18934                 on_el.on(eventOut, this.leave, this);
18935             }
18936         }, this);
18937         
18938     },
18939     
18940     
18941     // private
18942     timeout : null,
18943     hoverState : null,
18944     
18945     toggle : function () {
18946         this.hoverState == 'in' ? this.leave() : this.enter();
18947     },
18948     
18949     enter : function () {
18950         
18951         clearTimeout(this.timeout);
18952     
18953         this.hoverState = 'in';
18954     
18955         if (!this.delay || !this.delay.show) {
18956             this.show();
18957             return;
18958         }
18959         var _t = this;
18960         this.timeout = setTimeout(function () {
18961             if (_t.hoverState == 'in') {
18962                 _t.show();
18963             }
18964         }, this.delay.show)
18965     },
18966     
18967     leave : function() {
18968         clearTimeout(this.timeout);
18969     
18970         this.hoverState = 'out';
18971     
18972         if (!this.delay || !this.delay.hide) {
18973             this.hide();
18974             return;
18975         }
18976         var _t = this;
18977         this.timeout = setTimeout(function () {
18978             if (_t.hoverState == 'out') {
18979                 _t.hide();
18980             }
18981         }, this.delay.hide)
18982     },
18983     
18984     show : function (on_el)
18985     {
18986         if (!on_el) {
18987             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18988         }
18989         
18990         // set content.
18991         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18992         if (this.html !== false) {
18993             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18994         }
18995         this.el.removeClass([
18996             'fade','top','bottom', 'left', 'right','in',
18997             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18998         ]);
18999         if (!this.title.length) {
19000             this.el.select('.popover-title',true).hide();
19001         }
19002         
19003         var placement = typeof this.placement == 'function' ?
19004             this.placement.call(this, this.el, on_el) :
19005             this.placement;
19006             
19007         var autoToken = /\s?auto?\s?/i;
19008         var autoPlace = autoToken.test(placement);
19009         if (autoPlace) {
19010             placement = placement.replace(autoToken, '') || 'top';
19011         }
19012         
19013         //this.el.detach()
19014         //this.el.setXY([0,0]);
19015         this.el.show();
19016         this.el.dom.style.display='block';
19017         this.el.addClass(placement);
19018         
19019         //this.el.appendTo(on_el);
19020         
19021         var p = this.getPosition();
19022         var box = this.el.getBox();
19023         
19024         if (autoPlace) {
19025             // fixme..
19026         }
19027         var align = Roo.bootstrap.Popover.alignment[placement];
19028         
19029 //        Roo.log(align);
19030         this.el.alignTo(on_el, align[0],align[1]);
19031         //var arrow = this.el.select('.arrow',true).first();
19032         //arrow.set(align[2], 
19033         
19034         this.el.addClass('in');
19035         
19036         
19037         if (this.el.hasClass('fade')) {
19038             // fade it?
19039         }
19040         
19041         this.hoverState = 'in';
19042         
19043         this.fireEvent('show', this);
19044         
19045     },
19046     hide : function()
19047     {
19048         this.el.setXY([0,0]);
19049         this.el.removeClass('in');
19050         this.el.hide();
19051         this.hoverState = null;
19052         
19053         this.fireEvent('hide', this);
19054     }
19055     
19056 });
19057
19058 Roo.bootstrap.Popover.alignment = {
19059     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19060     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19061     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19062     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19063 };
19064
19065  /*
19066  * - LGPL
19067  *
19068  * Progress
19069  * 
19070  */
19071
19072 /**
19073  * @class Roo.bootstrap.Progress
19074  * @extends Roo.bootstrap.Component
19075  * Bootstrap Progress class
19076  * @cfg {Boolean} striped striped of the progress bar
19077  * @cfg {Boolean} active animated of the progress bar
19078  * 
19079  * 
19080  * @constructor
19081  * Create a new Progress
19082  * @param {Object} config The config object
19083  */
19084
19085 Roo.bootstrap.Progress = function(config){
19086     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19087 };
19088
19089 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19090     
19091     striped : false,
19092     active: false,
19093     
19094     getAutoCreate : function(){
19095         var cfg = {
19096             tag: 'div',
19097             cls: 'progress'
19098         };
19099         
19100         
19101         if(this.striped){
19102             cfg.cls += ' progress-striped';
19103         }
19104       
19105         if(this.active){
19106             cfg.cls += ' active';
19107         }
19108         
19109         
19110         return cfg;
19111     }
19112    
19113 });
19114
19115  
19116
19117  /*
19118  * - LGPL
19119  *
19120  * ProgressBar
19121  * 
19122  */
19123
19124 /**
19125  * @class Roo.bootstrap.ProgressBar
19126  * @extends Roo.bootstrap.Component
19127  * Bootstrap ProgressBar class
19128  * @cfg {Number} aria_valuenow aria-value now
19129  * @cfg {Number} aria_valuemin aria-value min
19130  * @cfg {Number} aria_valuemax aria-value max
19131  * @cfg {String} label label for the progress bar
19132  * @cfg {String} panel (success | info | warning | danger )
19133  * @cfg {String} role role of the progress bar
19134  * @cfg {String} sr_only text
19135  * 
19136  * 
19137  * @constructor
19138  * Create a new ProgressBar
19139  * @param {Object} config The config object
19140  */
19141
19142 Roo.bootstrap.ProgressBar = function(config){
19143     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19144 };
19145
19146 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19147     
19148     aria_valuenow : 0,
19149     aria_valuemin : 0,
19150     aria_valuemax : 100,
19151     label : false,
19152     panel : false,
19153     role : false,
19154     sr_only: false,
19155     
19156     getAutoCreate : function()
19157     {
19158         
19159         var cfg = {
19160             tag: 'div',
19161             cls: 'progress-bar',
19162             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19163         };
19164         
19165         if(this.sr_only){
19166             cfg.cn = {
19167                 tag: 'span',
19168                 cls: 'sr-only',
19169                 html: this.sr_only
19170             }
19171         }
19172         
19173         if(this.role){
19174             cfg.role = this.role;
19175         }
19176         
19177         if(this.aria_valuenow){
19178             cfg['aria-valuenow'] = this.aria_valuenow;
19179         }
19180         
19181         if(this.aria_valuemin){
19182             cfg['aria-valuemin'] = this.aria_valuemin;
19183         }
19184         
19185         if(this.aria_valuemax){
19186             cfg['aria-valuemax'] = this.aria_valuemax;
19187         }
19188         
19189         if(this.label && !this.sr_only){
19190             cfg.html = this.label;
19191         }
19192         
19193         if(this.panel){
19194             cfg.cls += ' progress-bar-' + this.panel;
19195         }
19196         
19197         return cfg;
19198     },
19199     
19200     update : function(aria_valuenow)
19201     {
19202         this.aria_valuenow = aria_valuenow;
19203         
19204         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19205     }
19206    
19207 });
19208
19209  
19210
19211  /*
19212  * - LGPL
19213  *
19214  * column
19215  * 
19216  */
19217
19218 /**
19219  * @class Roo.bootstrap.TabGroup
19220  * @extends Roo.bootstrap.Column
19221  * Bootstrap Column class
19222  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19223  * @cfg {Boolean} carousel true to make the group behave like a carousel
19224  * @cfg {Boolean} bullets show bullets for the panels
19225  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19226  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19227  * @cfg {Boolean} showarrow (true|false) show arrow default true
19228  * 
19229  * @constructor
19230  * Create a new TabGroup
19231  * @param {Object} config The config object
19232  */
19233
19234 Roo.bootstrap.TabGroup = function(config){
19235     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19236     if (!this.navId) {
19237         this.navId = Roo.id();
19238     }
19239     this.tabs = [];
19240     Roo.bootstrap.TabGroup.register(this);
19241     
19242 };
19243
19244 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19245     
19246     carousel : false,
19247     transition : false,
19248     bullets : 0,
19249     timer : 0,
19250     autoslide : false,
19251     slideFn : false,
19252     slideOnTouch : false,
19253     showarrow : true,
19254     
19255     getAutoCreate : function()
19256     {
19257         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19258         
19259         cfg.cls += ' tab-content';
19260         
19261         if (this.carousel) {
19262             cfg.cls += ' carousel slide';
19263             
19264             cfg.cn = [{
19265                cls : 'carousel-inner',
19266                cn : []
19267             }];
19268         
19269             if(this.bullets  && !Roo.isTouch){
19270                 
19271                 var bullets = {
19272                     cls : 'carousel-bullets',
19273                     cn : []
19274                 };
19275                
19276                 if(this.bullets_cls){
19277                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19278                 }
19279                 
19280                 bullets.cn.push({
19281                     cls : 'clear'
19282                 });
19283                 
19284                 cfg.cn[0].cn.push(bullets);
19285             }
19286             
19287             if(this.showarrow){
19288                 cfg.cn[0].cn.push({
19289                     tag : 'div',
19290                     class : 'carousel-arrow',
19291                     cn : [
19292                         {
19293                             tag : 'div',
19294                             class : 'carousel-prev',
19295                             cn : [
19296                                 {
19297                                     tag : 'i',
19298                                     class : 'fa fa-chevron-left'
19299                                 }
19300                             ]
19301                         },
19302                         {
19303                             tag : 'div',
19304                             class : 'carousel-next',
19305                             cn : [
19306                                 {
19307                                     tag : 'i',
19308                                     class : 'fa fa-chevron-right'
19309                                 }
19310                             ]
19311                         }
19312                     ]
19313                 });
19314             }
19315             
19316         }
19317         
19318         return cfg;
19319     },
19320     
19321     initEvents:  function()
19322     {
19323 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19324 //            this.el.on("touchstart", this.onTouchStart, this);
19325 //        }
19326         
19327         if(this.autoslide){
19328             var _this = this;
19329             
19330             this.slideFn = window.setInterval(function() {
19331                 _this.showPanelNext();
19332             }, this.timer);
19333         }
19334         
19335         if(this.showarrow){
19336             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19337             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19338         }
19339         
19340         
19341     },
19342     
19343 //    onTouchStart : function(e, el, o)
19344 //    {
19345 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19346 //            return;
19347 //        }
19348 //        
19349 //        this.showPanelNext();
19350 //    },
19351     
19352     
19353     getChildContainer : function()
19354     {
19355         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19356     },
19357     
19358     /**
19359     * register a Navigation item
19360     * @param {Roo.bootstrap.NavItem} the navitem to add
19361     */
19362     register : function(item)
19363     {
19364         this.tabs.push( item);
19365         item.navId = this.navId; // not really needed..
19366         this.addBullet();
19367     
19368     },
19369     
19370     getActivePanel : function()
19371     {
19372         var r = false;
19373         Roo.each(this.tabs, function(t) {
19374             if (t.active) {
19375                 r = t;
19376                 return false;
19377             }
19378             return null;
19379         });
19380         return r;
19381         
19382     },
19383     getPanelByName : function(n)
19384     {
19385         var r = false;
19386         Roo.each(this.tabs, function(t) {
19387             if (t.tabId == n) {
19388                 r = t;
19389                 return false;
19390             }
19391             return null;
19392         });
19393         return r;
19394     },
19395     indexOfPanel : function(p)
19396     {
19397         var r = false;
19398         Roo.each(this.tabs, function(t,i) {
19399             if (t.tabId == p.tabId) {
19400                 r = i;
19401                 return false;
19402             }
19403             return null;
19404         });
19405         return r;
19406     },
19407     /**
19408      * show a specific panel
19409      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19410      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19411      */
19412     showPanel : function (pan)
19413     {
19414         if(this.transition || typeof(pan) == 'undefined'){
19415             Roo.log("waiting for the transitionend");
19416             return false;
19417         }
19418         
19419         if (typeof(pan) == 'number') {
19420             pan = this.tabs[pan];
19421         }
19422         
19423         if (typeof(pan) == 'string') {
19424             pan = this.getPanelByName(pan);
19425         }
19426         
19427         var cur = this.getActivePanel();
19428         
19429         if(!pan || !cur){
19430             Roo.log('pan or acitve pan is undefined');
19431             return false;
19432         }
19433         
19434         if (pan.tabId == this.getActivePanel().tabId) {
19435             return true;
19436         }
19437         
19438         if (false === cur.fireEvent('beforedeactivate')) {
19439             return false;
19440         }
19441         
19442         if(this.bullets > 0 && !Roo.isTouch){
19443             this.setActiveBullet(this.indexOfPanel(pan));
19444         }
19445         
19446         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19447             
19448             //class="carousel-item carousel-item-next carousel-item-left"
19449             
19450             this.transition = true;
19451             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19452             var lr = dir == 'next' ? 'left' : 'right';
19453             pan.el.addClass(dir); // or prev
19454             pan.el.addClass('carousel-item-' + dir); // or prev
19455             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19456             cur.el.addClass(lr); // or right
19457             pan.el.addClass(lr);
19458             cur.el.addClass('carousel-item-' +lr); // or right
19459             pan.el.addClass('carousel-item-' +lr);
19460             
19461             
19462             var _this = this;
19463             cur.el.on('transitionend', function() {
19464                 Roo.log("trans end?");
19465                 
19466                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19467                 pan.setActive(true);
19468                 
19469                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19470                 cur.setActive(false);
19471                 
19472                 _this.transition = false;
19473                 
19474             }, this, { single:  true } );
19475             
19476             return true;
19477         }
19478         
19479         cur.setActive(false);
19480         pan.setActive(true);
19481         
19482         return true;
19483         
19484     },
19485     showPanelNext : function()
19486     {
19487         var i = this.indexOfPanel(this.getActivePanel());
19488         
19489         if (i >= this.tabs.length - 1 && !this.autoslide) {
19490             return;
19491         }
19492         
19493         if (i >= this.tabs.length - 1 && this.autoslide) {
19494             i = -1;
19495         }
19496         
19497         this.showPanel(this.tabs[i+1]);
19498     },
19499     
19500     showPanelPrev : function()
19501     {
19502         var i = this.indexOfPanel(this.getActivePanel());
19503         
19504         if (i  < 1 && !this.autoslide) {
19505             return;
19506         }
19507         
19508         if (i < 1 && this.autoslide) {
19509             i = this.tabs.length;
19510         }
19511         
19512         this.showPanel(this.tabs[i-1]);
19513     },
19514     
19515     
19516     addBullet: function()
19517     {
19518         if(!this.bullets || Roo.isTouch){
19519             return;
19520         }
19521         var ctr = this.el.select('.carousel-bullets',true).first();
19522         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19523         var bullet = ctr.createChild({
19524             cls : 'bullet bullet-' + i
19525         },ctr.dom.lastChild);
19526         
19527         
19528         var _this = this;
19529         
19530         bullet.on('click', (function(e, el, o, ii, t){
19531
19532             e.preventDefault();
19533
19534             this.showPanel(ii);
19535
19536             if(this.autoslide && this.slideFn){
19537                 clearInterval(this.slideFn);
19538                 this.slideFn = window.setInterval(function() {
19539                     _this.showPanelNext();
19540                 }, this.timer);
19541             }
19542
19543         }).createDelegate(this, [i, bullet], true));
19544                 
19545         
19546     },
19547      
19548     setActiveBullet : function(i)
19549     {
19550         if(Roo.isTouch){
19551             return;
19552         }
19553         
19554         Roo.each(this.el.select('.bullet', true).elements, function(el){
19555             el.removeClass('selected');
19556         });
19557
19558         var bullet = this.el.select('.bullet-' + i, true).first();
19559         
19560         if(!bullet){
19561             return;
19562         }
19563         
19564         bullet.addClass('selected');
19565     }
19566     
19567     
19568   
19569 });
19570
19571  
19572
19573  
19574  
19575 Roo.apply(Roo.bootstrap.TabGroup, {
19576     
19577     groups: {},
19578      /**
19579     * register a Navigation Group
19580     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19581     */
19582     register : function(navgrp)
19583     {
19584         this.groups[navgrp.navId] = navgrp;
19585         
19586     },
19587     /**
19588     * fetch a Navigation Group based on the navigation ID
19589     * if one does not exist , it will get created.
19590     * @param {string} the navgroup to add
19591     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19592     */
19593     get: function(navId) {
19594         if (typeof(this.groups[navId]) == 'undefined') {
19595             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19596         }
19597         return this.groups[navId] ;
19598     }
19599     
19600     
19601     
19602 });
19603
19604  /*
19605  * - LGPL
19606  *
19607  * TabPanel
19608  * 
19609  */
19610
19611 /**
19612  * @class Roo.bootstrap.TabPanel
19613  * @extends Roo.bootstrap.Component
19614  * Bootstrap TabPanel class
19615  * @cfg {Boolean} active panel active
19616  * @cfg {String} html panel content
19617  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19618  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19619  * @cfg {String} href click to link..
19620  * 
19621  * 
19622  * @constructor
19623  * Create a new TabPanel
19624  * @param {Object} config The config object
19625  */
19626
19627 Roo.bootstrap.TabPanel = function(config){
19628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19629     this.addEvents({
19630         /**
19631              * @event changed
19632              * Fires when the active status changes
19633              * @param {Roo.bootstrap.TabPanel} this
19634              * @param {Boolean} state the new state
19635             
19636          */
19637         'changed': true,
19638         /**
19639              * @event beforedeactivate
19640              * Fires before a tab is de-activated - can be used to do validation on a form.
19641              * @param {Roo.bootstrap.TabPanel} this
19642              * @return {Boolean} false if there is an error
19643             
19644          */
19645         'beforedeactivate': true
19646      });
19647     
19648     this.tabId = this.tabId || Roo.id();
19649   
19650 };
19651
19652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19653     
19654     active: false,
19655     html: false,
19656     tabId: false,
19657     navId : false,
19658     href : '',
19659     
19660     getAutoCreate : function(){
19661         
19662         
19663         var cfg = {
19664             tag: 'div',
19665             // item is needed for carousel - not sure if it has any effect otherwise
19666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19667             html: this.html || ''
19668         };
19669         
19670         if(this.active){
19671             cfg.cls += ' active';
19672         }
19673         
19674         if(this.tabId){
19675             cfg.tabId = this.tabId;
19676         }
19677         
19678         
19679         
19680         return cfg;
19681     },
19682     
19683     initEvents:  function()
19684     {
19685         var p = this.parent();
19686         
19687         this.navId = this.navId || p.navId;
19688         
19689         if (typeof(this.navId) != 'undefined') {
19690             // not really needed.. but just in case.. parent should be a NavGroup.
19691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19692             
19693             tg.register(this);
19694             
19695             var i = tg.tabs.length - 1;
19696             
19697             if(this.active && tg.bullets > 0 && i < tg.bullets){
19698                 tg.setActiveBullet(i);
19699             }
19700         }
19701         
19702         this.el.on('click', this.onClick, this);
19703         
19704         if(Roo.isTouch){
19705             this.el.on("touchstart", this.onTouchStart, this);
19706             this.el.on("touchmove", this.onTouchMove, this);
19707             this.el.on("touchend", this.onTouchEnd, this);
19708         }
19709         
19710     },
19711     
19712     onRender : function(ct, position)
19713     {
19714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19715     },
19716     
19717     setActive : function(state)
19718     {
19719         Roo.log("panel - set active " + this.tabId + "=" + state);
19720         
19721         this.active = state;
19722         if (!state) {
19723             this.el.removeClass('active');
19724             
19725         } else  if (!this.el.hasClass('active')) {
19726             this.el.addClass('active');
19727         }
19728         
19729         this.fireEvent('changed', this, state);
19730     },
19731     
19732     onClick : function(e)
19733     {
19734         e.preventDefault();
19735         
19736         if(!this.href.length){
19737             return;
19738         }
19739         
19740         window.location.href = this.href;
19741     },
19742     
19743     startX : 0,
19744     startY : 0,
19745     endX : 0,
19746     endY : 0,
19747     swiping : false,
19748     
19749     onTouchStart : function(e)
19750     {
19751         this.swiping = false;
19752         
19753         this.startX = e.browserEvent.touches[0].clientX;
19754         this.startY = e.browserEvent.touches[0].clientY;
19755     },
19756     
19757     onTouchMove : function(e)
19758     {
19759         this.swiping = true;
19760         
19761         this.endX = e.browserEvent.touches[0].clientX;
19762         this.endY = e.browserEvent.touches[0].clientY;
19763     },
19764     
19765     onTouchEnd : function(e)
19766     {
19767         if(!this.swiping){
19768             this.onClick(e);
19769             return;
19770         }
19771         
19772         var tabGroup = this.parent();
19773         
19774         if(this.endX > this.startX){ // swiping right
19775             tabGroup.showPanelPrev();
19776             return;
19777         }
19778         
19779         if(this.startX > this.endX){ // swiping left
19780             tabGroup.showPanelNext();
19781             return;
19782         }
19783     }
19784     
19785     
19786 });
19787  
19788
19789  
19790
19791  /*
19792  * - LGPL
19793  *
19794  * DateField
19795  * 
19796  */
19797
19798 /**
19799  * @class Roo.bootstrap.DateField
19800  * @extends Roo.bootstrap.Input
19801  * Bootstrap DateField class
19802  * @cfg {Number} weekStart default 0
19803  * @cfg {String} viewMode default empty, (months|years)
19804  * @cfg {String} minViewMode default empty, (months|years)
19805  * @cfg {Number} startDate default -Infinity
19806  * @cfg {Number} endDate default Infinity
19807  * @cfg {Boolean} todayHighlight default false
19808  * @cfg {Boolean} todayBtn default false
19809  * @cfg {Boolean} calendarWeeks default false
19810  * @cfg {Object} daysOfWeekDisabled default empty
19811  * @cfg {Boolean} singleMode default false (true | false)
19812  * 
19813  * @cfg {Boolean} keyboardNavigation default true
19814  * @cfg {String} language default en
19815  * 
19816  * @constructor
19817  * Create a new DateField
19818  * @param {Object} config The config object
19819  */
19820
19821 Roo.bootstrap.DateField = function(config){
19822     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19823      this.addEvents({
19824             /**
19825              * @event show
19826              * Fires when this field show.
19827              * @param {Roo.bootstrap.DateField} this
19828              * @param {Mixed} date The date value
19829              */
19830             show : true,
19831             /**
19832              * @event show
19833              * Fires when this field hide.
19834              * @param {Roo.bootstrap.DateField} this
19835              * @param {Mixed} date The date value
19836              */
19837             hide : true,
19838             /**
19839              * @event select
19840              * Fires when select a date.
19841              * @param {Roo.bootstrap.DateField} this
19842              * @param {Mixed} date The date value
19843              */
19844             select : true,
19845             /**
19846              * @event beforeselect
19847              * Fires when before select a date.
19848              * @param {Roo.bootstrap.DateField} this
19849              * @param {Mixed} date The date value
19850              */
19851             beforeselect : true
19852         });
19853 };
19854
19855 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19856     
19857     /**
19858      * @cfg {String} format
19859      * The default date format string which can be overriden for localization support.  The format must be
19860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19861      */
19862     format : "m/d/y",
19863     /**
19864      * @cfg {String} altFormats
19865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19867      */
19868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19869     
19870     weekStart : 0,
19871     
19872     viewMode : '',
19873     
19874     minViewMode : '',
19875     
19876     todayHighlight : false,
19877     
19878     todayBtn: false,
19879     
19880     language: 'en',
19881     
19882     keyboardNavigation: true,
19883     
19884     calendarWeeks: false,
19885     
19886     startDate: -Infinity,
19887     
19888     endDate: Infinity,
19889     
19890     daysOfWeekDisabled: [],
19891     
19892     _events: [],
19893     
19894     singleMode : false,
19895     
19896     UTCDate: function()
19897     {
19898         return new Date(Date.UTC.apply(Date, arguments));
19899     },
19900     
19901     UTCToday: function()
19902     {
19903         var today = new Date();
19904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19905     },
19906     
19907     getDate: function() {
19908             var d = this.getUTCDate();
19909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19910     },
19911     
19912     getUTCDate: function() {
19913             return this.date;
19914     },
19915     
19916     setDate: function(d) {
19917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19918     },
19919     
19920     setUTCDate: function(d) {
19921             this.date = d;
19922             this.setValue(this.formatDate(this.date));
19923     },
19924         
19925     onRender: function(ct, position)
19926     {
19927         
19928         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19929         
19930         this.language = this.language || 'en';
19931         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19932         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19933         
19934         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19935         this.format = this.format || 'm/d/y';
19936         this.isInline = false;
19937         this.isInput = true;
19938         this.component = this.el.select('.add-on', true).first() || false;
19939         this.component = (this.component && this.component.length === 0) ? false : this.component;
19940         this.hasInput = this.component && this.inputEl().length;
19941         
19942         if (typeof(this.minViewMode === 'string')) {
19943             switch (this.minViewMode) {
19944                 case 'months':
19945                     this.minViewMode = 1;
19946                     break;
19947                 case 'years':
19948                     this.minViewMode = 2;
19949                     break;
19950                 default:
19951                     this.minViewMode = 0;
19952                     break;
19953             }
19954         }
19955         
19956         if (typeof(this.viewMode === 'string')) {
19957             switch (this.viewMode) {
19958                 case 'months':
19959                     this.viewMode = 1;
19960                     break;
19961                 case 'years':
19962                     this.viewMode = 2;
19963                     break;
19964                 default:
19965                     this.viewMode = 0;
19966                     break;
19967             }
19968         }
19969                 
19970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19971         
19972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19973         
19974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19975         
19976         this.picker().on('mousedown', this.onMousedown, this);
19977         this.picker().on('click', this.onClick, this);
19978         
19979         this.picker().addClass('datepicker-dropdown');
19980         
19981         this.startViewMode = this.viewMode;
19982         
19983         if(this.singleMode){
19984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19985                 v.setVisibilityMode(Roo.Element.DISPLAY);
19986                 v.hide();
19987             });
19988             
19989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19990                 v.setStyle('width', '189px');
19991             });
19992         }
19993         
19994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19995             if(!this.calendarWeeks){
19996                 v.remove();
19997                 return;
19998             }
19999             
20000             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20001             v.attr('colspan', function(i, val){
20002                 return parseInt(val) + 1;
20003             });
20004         });
20005                         
20006         
20007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20008         
20009         this.setStartDate(this.startDate);
20010         this.setEndDate(this.endDate);
20011         
20012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20013         
20014         this.fillDow();
20015         this.fillMonths();
20016         this.update();
20017         this.showMode();
20018         
20019         if(this.isInline) {
20020             this.showPopup();
20021         }
20022     },
20023     
20024     picker : function()
20025     {
20026         return this.pickerEl;
20027 //        return this.el.select('.datepicker', true).first();
20028     },
20029     
20030     fillDow: function()
20031     {
20032         var dowCnt = this.weekStart;
20033         
20034         var dow = {
20035             tag: 'tr',
20036             cn: [
20037                 
20038             ]
20039         };
20040         
20041         if(this.calendarWeeks){
20042             dow.cn.push({
20043                 tag: 'th',
20044                 cls: 'cw',
20045                 html: '&nbsp;'
20046             })
20047         }
20048         
20049         while (dowCnt < this.weekStart + 7) {
20050             dow.cn.push({
20051                 tag: 'th',
20052                 cls: 'dow',
20053                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20054             });
20055         }
20056         
20057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20058     },
20059     
20060     fillMonths: function()
20061     {    
20062         var i = 0;
20063         var months = this.picker().select('>.datepicker-months td', true).first();
20064         
20065         months.dom.innerHTML = '';
20066         
20067         while (i < 12) {
20068             var month = {
20069                 tag: 'span',
20070                 cls: 'month',
20071                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20072             };
20073             
20074             months.createChild(month);
20075         }
20076         
20077     },
20078     
20079     update: function()
20080     {
20081         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;
20082         
20083         if (this.date < this.startDate) {
20084             this.viewDate = new Date(this.startDate);
20085         } else if (this.date > this.endDate) {
20086             this.viewDate = new Date(this.endDate);
20087         } else {
20088             this.viewDate = new Date(this.date);
20089         }
20090         
20091         this.fill();
20092     },
20093     
20094     fill: function() 
20095     {
20096         var d = new Date(this.viewDate),
20097                 year = d.getUTCFullYear(),
20098                 month = d.getUTCMonth(),
20099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20103                 currentDate = this.date && this.date.valueOf(),
20104                 today = this.UTCToday();
20105         
20106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20107         
20108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20109         
20110 //        this.picker.select('>tfoot th.today').
20111 //                                              .text(dates[this.language].today)
20112 //                                              .toggle(this.todayBtn !== false);
20113     
20114         this.updateNavArrows();
20115         this.fillMonths();
20116                                                 
20117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20118         
20119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20120          
20121         prevMonth.setUTCDate(day);
20122         
20123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20124         
20125         var nextMonth = new Date(prevMonth);
20126         
20127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20128         
20129         nextMonth = nextMonth.valueOf();
20130         
20131         var fillMonths = false;
20132         
20133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20134         
20135         while(prevMonth.valueOf() <= nextMonth) {
20136             var clsName = '';
20137             
20138             if (prevMonth.getUTCDay() === this.weekStart) {
20139                 if(fillMonths){
20140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20141                 }
20142                     
20143                 fillMonths = {
20144                     tag: 'tr',
20145                     cn: []
20146                 };
20147                 
20148                 if(this.calendarWeeks){
20149                     // ISO 8601: First week contains first thursday.
20150                     // ISO also states week starts on Monday, but we can be more abstract here.
20151                     var
20152                     // Start of current week: based on weekstart/current date
20153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20154                     // Thursday of this week
20155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20156                     // First Thursday of year, year from thursday
20157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20160                     
20161                     fillMonths.cn.push({
20162                         tag: 'td',
20163                         cls: 'cw',
20164                         html: calWeek
20165                     });
20166                 }
20167             }
20168             
20169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20170                 clsName += ' old';
20171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20172                 clsName += ' new';
20173             }
20174             if (this.todayHighlight &&
20175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20176                 prevMonth.getUTCMonth() == today.getMonth() &&
20177                 prevMonth.getUTCDate() == today.getDate()) {
20178                 clsName += ' today';
20179             }
20180             
20181             if (currentDate && prevMonth.valueOf() === currentDate) {
20182                 clsName += ' active';
20183             }
20184             
20185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20187                     clsName += ' disabled';
20188             }
20189             
20190             fillMonths.cn.push({
20191                 tag: 'td',
20192                 cls: 'day ' + clsName,
20193                 html: prevMonth.getDate()
20194             });
20195             
20196             prevMonth.setDate(prevMonth.getDate()+1);
20197         }
20198           
20199         var currentYear = this.date && this.date.getUTCFullYear();
20200         var currentMonth = this.date && this.date.getUTCMonth();
20201         
20202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20203         
20204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20205             v.removeClass('active');
20206             
20207             if(currentYear === year && k === currentMonth){
20208                 v.addClass('active');
20209             }
20210             
20211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20212                 v.addClass('disabled');
20213             }
20214             
20215         });
20216         
20217         
20218         year = parseInt(year/10, 10) * 10;
20219         
20220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20221         
20222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20223         
20224         year -= 1;
20225         for (var i = -1; i < 11; i++) {
20226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20227                 tag: 'span',
20228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20229                 html: year
20230             });
20231             
20232             year += 1;
20233         }
20234     },
20235     
20236     showMode: function(dir) 
20237     {
20238         if (dir) {
20239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20240         }
20241         
20242         Roo.each(this.picker().select('>div',true).elements, function(v){
20243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20244             v.hide();
20245         });
20246         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20247     },
20248     
20249     place: function()
20250     {
20251         if(this.isInline) {
20252             return;
20253         }
20254         
20255         this.picker().removeClass(['bottom', 'top']);
20256         
20257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20258             /*
20259              * place to the top of element!
20260              *
20261              */
20262             
20263             this.picker().addClass('top');
20264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20265             
20266             return;
20267         }
20268         
20269         this.picker().addClass('bottom');
20270         
20271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20272     },
20273     
20274     parseDate : function(value)
20275     {
20276         if(!value || value instanceof Date){
20277             return value;
20278         }
20279         var v = Date.parseDate(value, this.format);
20280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20281             v = Date.parseDate(value, 'Y-m-d');
20282         }
20283         if(!v && this.altFormats){
20284             if(!this.altFormatsArray){
20285                 this.altFormatsArray = this.altFormats.split("|");
20286             }
20287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20288                 v = Date.parseDate(value, this.altFormatsArray[i]);
20289             }
20290         }
20291         return v;
20292     },
20293     
20294     formatDate : function(date, fmt)
20295     {   
20296         return (!date || !(date instanceof Date)) ?
20297         date : date.dateFormat(fmt || this.format);
20298     },
20299     
20300     onFocus : function()
20301     {
20302         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20303         this.showPopup();
20304     },
20305     
20306     onBlur : function()
20307     {
20308         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20309         
20310         var d = this.inputEl().getValue();
20311         
20312         this.setValue(d);
20313                 
20314         this.hidePopup();
20315     },
20316     
20317     showPopup : function()
20318     {
20319         this.picker().show();
20320         this.update();
20321         this.place();
20322         
20323         this.fireEvent('showpopup', this, this.date);
20324     },
20325     
20326     hidePopup : function()
20327     {
20328         if(this.isInline) {
20329             return;
20330         }
20331         this.picker().hide();
20332         this.viewMode = this.startViewMode;
20333         this.showMode();
20334         
20335         this.fireEvent('hidepopup', this, this.date);
20336         
20337     },
20338     
20339     onMousedown: function(e)
20340     {
20341         e.stopPropagation();
20342         e.preventDefault();
20343     },
20344     
20345     keyup: function(e)
20346     {
20347         Roo.bootstrap.DateField.superclass.keyup.call(this);
20348         this.update();
20349     },
20350
20351     setValue: function(v)
20352     {
20353         if(this.fireEvent('beforeselect', this, v) !== false){
20354             var d = new Date(this.parseDate(v) ).clearTime();
20355         
20356             if(isNaN(d.getTime())){
20357                 this.date = this.viewDate = '';
20358                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20359                 return;
20360             }
20361
20362             v = this.formatDate(d);
20363
20364             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20365
20366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20367
20368             this.update();
20369
20370             this.fireEvent('select', this, this.date);
20371         }
20372     },
20373     
20374     getValue: function()
20375     {
20376         return this.formatDate(this.date);
20377     },
20378     
20379     fireKey: function(e)
20380     {
20381         if (!this.picker().isVisible()){
20382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20383                 this.showPopup();
20384             }
20385             return;
20386         }
20387         
20388         var dateChanged = false,
20389         dir, day, month,
20390         newDate, newViewDate;
20391         
20392         switch(e.keyCode){
20393             case 27: // escape
20394                 this.hidePopup();
20395                 e.preventDefault();
20396                 break;
20397             case 37: // left
20398             case 39: // right
20399                 if (!this.keyboardNavigation) {
20400                     break;
20401                 }
20402                 dir = e.keyCode == 37 ? -1 : 1;
20403                 
20404                 if (e.ctrlKey){
20405                     newDate = this.moveYear(this.date, dir);
20406                     newViewDate = this.moveYear(this.viewDate, dir);
20407                 } else if (e.shiftKey){
20408                     newDate = this.moveMonth(this.date, dir);
20409                     newViewDate = this.moveMonth(this.viewDate, dir);
20410                 } else {
20411                     newDate = new Date(this.date);
20412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20413                     newViewDate = new Date(this.viewDate);
20414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20415                 }
20416                 if (this.dateWithinRange(newDate)){
20417                     this.date = newDate;
20418                     this.viewDate = newViewDate;
20419                     this.setValue(this.formatDate(this.date));
20420 //                    this.update();
20421                     e.preventDefault();
20422                     dateChanged = true;
20423                 }
20424                 break;
20425             case 38: // up
20426             case 40: // down
20427                 if (!this.keyboardNavigation) {
20428                     break;
20429                 }
20430                 dir = e.keyCode == 38 ? -1 : 1;
20431                 if (e.ctrlKey){
20432                     newDate = this.moveYear(this.date, dir);
20433                     newViewDate = this.moveYear(this.viewDate, dir);
20434                 } else if (e.shiftKey){
20435                     newDate = this.moveMonth(this.date, dir);
20436                     newViewDate = this.moveMonth(this.viewDate, dir);
20437                 } else {
20438                     newDate = new Date(this.date);
20439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20440                     newViewDate = new Date(this.viewDate);
20441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20442                 }
20443                 if (this.dateWithinRange(newDate)){
20444                     this.date = newDate;
20445                     this.viewDate = newViewDate;
20446                     this.setValue(this.formatDate(this.date));
20447 //                    this.update();
20448                     e.preventDefault();
20449                     dateChanged = true;
20450                 }
20451                 break;
20452             case 13: // enter
20453                 this.setValue(this.formatDate(this.date));
20454                 this.hidePopup();
20455                 e.preventDefault();
20456                 break;
20457             case 9: // tab
20458                 this.setValue(this.formatDate(this.date));
20459                 this.hidePopup();
20460                 break;
20461             case 16: // shift
20462             case 17: // ctrl
20463             case 18: // alt
20464                 break;
20465             default :
20466                 this.hidePopup();
20467                 
20468         }
20469     },
20470     
20471     
20472     onClick: function(e) 
20473     {
20474         e.stopPropagation();
20475         e.preventDefault();
20476         
20477         var target = e.getTarget();
20478         
20479         if(target.nodeName.toLowerCase() === 'i'){
20480             target = Roo.get(target).dom.parentNode;
20481         }
20482         
20483         var nodeName = target.nodeName;
20484         var className = target.className;
20485         var html = target.innerHTML;
20486         //Roo.log(nodeName);
20487         
20488         switch(nodeName.toLowerCase()) {
20489             case 'th':
20490                 switch(className) {
20491                     case 'switch':
20492                         this.showMode(1);
20493                         break;
20494                     case 'prev':
20495                     case 'next':
20496                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20497                         switch(this.viewMode){
20498                                 case 0:
20499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20500                                         break;
20501                                 case 1:
20502                                 case 2:
20503                                         this.viewDate = this.moveYear(this.viewDate, dir);
20504                                         break;
20505                         }
20506                         this.fill();
20507                         break;
20508                     case 'today':
20509                         var date = new Date();
20510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20511 //                        this.fill()
20512                         this.setValue(this.formatDate(this.date));
20513                         
20514                         this.hidePopup();
20515                         break;
20516                 }
20517                 break;
20518             case 'span':
20519                 if (className.indexOf('disabled') < 0) {
20520                     this.viewDate.setUTCDate(1);
20521                     if (className.indexOf('month') > -1) {
20522                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20523                     } else {
20524                         var year = parseInt(html, 10) || 0;
20525                         this.viewDate.setUTCFullYear(year);
20526                         
20527                     }
20528                     
20529                     if(this.singleMode){
20530                         this.setValue(this.formatDate(this.viewDate));
20531                         this.hidePopup();
20532                         return;
20533                     }
20534                     
20535                     this.showMode(-1);
20536                     this.fill();
20537                 }
20538                 break;
20539                 
20540             case 'td':
20541                 //Roo.log(className);
20542                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20543                     var day = parseInt(html, 10) || 1;
20544                     var year = this.viewDate.getUTCFullYear(),
20545                         month = this.viewDate.getUTCMonth();
20546
20547                     if (className.indexOf('old') > -1) {
20548                         if(month === 0 ){
20549                             month = 11;
20550                             year -= 1;
20551                         }else{
20552                             month -= 1;
20553                         }
20554                     } else if (className.indexOf('new') > -1) {
20555                         if (month == 11) {
20556                             month = 0;
20557                             year += 1;
20558                         } else {
20559                             month += 1;
20560                         }
20561                     }
20562                     //Roo.log([year,month,day]);
20563                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20564                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20565 //                    this.fill();
20566                     //Roo.log(this.formatDate(this.date));
20567                     this.setValue(this.formatDate(this.date));
20568                     this.hidePopup();
20569                 }
20570                 break;
20571         }
20572     },
20573     
20574     setStartDate: function(startDate)
20575     {
20576         this.startDate = startDate || -Infinity;
20577         if (this.startDate !== -Infinity) {
20578             this.startDate = this.parseDate(this.startDate);
20579         }
20580         this.update();
20581         this.updateNavArrows();
20582     },
20583
20584     setEndDate: function(endDate)
20585     {
20586         this.endDate = endDate || Infinity;
20587         if (this.endDate !== Infinity) {
20588             this.endDate = this.parseDate(this.endDate);
20589         }
20590         this.update();
20591         this.updateNavArrows();
20592     },
20593     
20594     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20595     {
20596         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20597         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20598             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20599         }
20600         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20601             return parseInt(d, 10);
20602         });
20603         this.update();
20604         this.updateNavArrows();
20605     },
20606     
20607     updateNavArrows: function() 
20608     {
20609         if(this.singleMode){
20610             return;
20611         }
20612         
20613         var d = new Date(this.viewDate),
20614         year = d.getUTCFullYear(),
20615         month = d.getUTCMonth();
20616         
20617         Roo.each(this.picker().select('.prev', true).elements, function(v){
20618             v.show();
20619             switch (this.viewMode) {
20620                 case 0:
20621
20622                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20623                         v.hide();
20624                     }
20625                     break;
20626                 case 1:
20627                 case 2:
20628                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20629                         v.hide();
20630                     }
20631                     break;
20632             }
20633         });
20634         
20635         Roo.each(this.picker().select('.next', true).elements, function(v){
20636             v.show();
20637             switch (this.viewMode) {
20638                 case 0:
20639
20640                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20641                         v.hide();
20642                     }
20643                     break;
20644                 case 1:
20645                 case 2:
20646                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20647                         v.hide();
20648                     }
20649                     break;
20650             }
20651         })
20652     },
20653     
20654     moveMonth: function(date, dir)
20655     {
20656         if (!dir) {
20657             return date;
20658         }
20659         var new_date = new Date(date.valueOf()),
20660         day = new_date.getUTCDate(),
20661         month = new_date.getUTCMonth(),
20662         mag = Math.abs(dir),
20663         new_month, test;
20664         dir = dir > 0 ? 1 : -1;
20665         if (mag == 1){
20666             test = dir == -1
20667             // If going back one month, make sure month is not current month
20668             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20669             ? function(){
20670                 return new_date.getUTCMonth() == month;
20671             }
20672             // If going forward one month, make sure month is as expected
20673             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20674             : function(){
20675                 return new_date.getUTCMonth() != new_month;
20676             };
20677             new_month = month + dir;
20678             new_date.setUTCMonth(new_month);
20679             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20680             if (new_month < 0 || new_month > 11) {
20681                 new_month = (new_month + 12) % 12;
20682             }
20683         } else {
20684             // For magnitudes >1, move one month at a time...
20685             for (var i=0; i<mag; i++) {
20686                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20687                 new_date = this.moveMonth(new_date, dir);
20688             }
20689             // ...then reset the day, keeping it in the new month
20690             new_month = new_date.getUTCMonth();
20691             new_date.setUTCDate(day);
20692             test = function(){
20693                 return new_month != new_date.getUTCMonth();
20694             };
20695         }
20696         // Common date-resetting loop -- if date is beyond end of month, make it
20697         // end of month
20698         while (test()){
20699             new_date.setUTCDate(--day);
20700             new_date.setUTCMonth(new_month);
20701         }
20702         return new_date;
20703     },
20704
20705     moveYear: function(date, dir)
20706     {
20707         return this.moveMonth(date, dir*12);
20708     },
20709
20710     dateWithinRange: function(date)
20711     {
20712         return date >= this.startDate && date <= this.endDate;
20713     },
20714
20715     
20716     remove: function() 
20717     {
20718         this.picker().remove();
20719     },
20720     
20721     validateValue : function(value)
20722     {
20723         if(this.getVisibilityEl().hasClass('hidden')){
20724             return true;
20725         }
20726         
20727         if(value.length < 1)  {
20728             if(this.allowBlank){
20729                 return true;
20730             }
20731             return false;
20732         }
20733         
20734         if(value.length < this.minLength){
20735             return false;
20736         }
20737         if(value.length > this.maxLength){
20738             return false;
20739         }
20740         if(this.vtype){
20741             var vt = Roo.form.VTypes;
20742             if(!vt[this.vtype](value, this)){
20743                 return false;
20744             }
20745         }
20746         if(typeof this.validator == "function"){
20747             var msg = this.validator(value);
20748             if(msg !== true){
20749                 return false;
20750             }
20751         }
20752         
20753         if(this.regex && !this.regex.test(value)){
20754             return false;
20755         }
20756         
20757         if(typeof(this.parseDate(value)) == 'undefined'){
20758             return false;
20759         }
20760         
20761         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20762             return false;
20763         }      
20764         
20765         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20766             return false;
20767         } 
20768         
20769         
20770         return true;
20771     },
20772     
20773     reset : function()
20774     {
20775         this.date = this.viewDate = '';
20776         
20777         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20778     }
20779    
20780 });
20781
20782 Roo.apply(Roo.bootstrap.DateField,  {
20783     
20784     head : {
20785         tag: 'thead',
20786         cn: [
20787         {
20788             tag: 'tr',
20789             cn: [
20790             {
20791                 tag: 'th',
20792                 cls: 'prev',
20793                 html: '<i class="fa fa-arrow-left"/>'
20794             },
20795             {
20796                 tag: 'th',
20797                 cls: 'switch',
20798                 colspan: '5'
20799             },
20800             {
20801                 tag: 'th',
20802                 cls: 'next',
20803                 html: '<i class="fa fa-arrow-right"/>'
20804             }
20805
20806             ]
20807         }
20808         ]
20809     },
20810     
20811     content : {
20812         tag: 'tbody',
20813         cn: [
20814         {
20815             tag: 'tr',
20816             cn: [
20817             {
20818                 tag: 'td',
20819                 colspan: '7'
20820             }
20821             ]
20822         }
20823         ]
20824     },
20825     
20826     footer : {
20827         tag: 'tfoot',
20828         cn: [
20829         {
20830             tag: 'tr',
20831             cn: [
20832             {
20833                 tag: 'th',
20834                 colspan: '7',
20835                 cls: 'today'
20836             }
20837                     
20838             ]
20839         }
20840         ]
20841     },
20842     
20843     dates:{
20844         en: {
20845             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20846             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20847             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20848             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20849             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20850             today: "Today"
20851         }
20852     },
20853     
20854     modes: [
20855     {
20856         clsName: 'days',
20857         navFnc: 'Month',
20858         navStep: 1
20859     },
20860     {
20861         clsName: 'months',
20862         navFnc: 'FullYear',
20863         navStep: 1
20864     },
20865     {
20866         clsName: 'years',
20867         navFnc: 'FullYear',
20868         navStep: 10
20869     }]
20870 });
20871
20872 Roo.apply(Roo.bootstrap.DateField,  {
20873   
20874     template : {
20875         tag: 'div',
20876         cls: 'datepicker dropdown-menu roo-dynamic',
20877         cn: [
20878         {
20879             tag: 'div',
20880             cls: 'datepicker-days',
20881             cn: [
20882             {
20883                 tag: 'table',
20884                 cls: 'table-condensed',
20885                 cn:[
20886                 Roo.bootstrap.DateField.head,
20887                 {
20888                     tag: 'tbody'
20889                 },
20890                 Roo.bootstrap.DateField.footer
20891                 ]
20892             }
20893             ]
20894         },
20895         {
20896             tag: 'div',
20897             cls: 'datepicker-months',
20898             cn: [
20899             {
20900                 tag: 'table',
20901                 cls: 'table-condensed',
20902                 cn:[
20903                 Roo.bootstrap.DateField.head,
20904                 Roo.bootstrap.DateField.content,
20905                 Roo.bootstrap.DateField.footer
20906                 ]
20907             }
20908             ]
20909         },
20910         {
20911             tag: 'div',
20912             cls: 'datepicker-years',
20913             cn: [
20914             {
20915                 tag: 'table',
20916                 cls: 'table-condensed',
20917                 cn:[
20918                 Roo.bootstrap.DateField.head,
20919                 Roo.bootstrap.DateField.content,
20920                 Roo.bootstrap.DateField.footer
20921                 ]
20922             }
20923             ]
20924         }
20925         ]
20926     }
20927 });
20928
20929  
20930
20931  /*
20932  * - LGPL
20933  *
20934  * TimeField
20935  * 
20936  */
20937
20938 /**
20939  * @class Roo.bootstrap.TimeField
20940  * @extends Roo.bootstrap.Input
20941  * Bootstrap DateField class
20942  * 
20943  * 
20944  * @constructor
20945  * Create a new TimeField
20946  * @param {Object} config The config object
20947  */
20948
20949 Roo.bootstrap.TimeField = function(config){
20950     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20951     this.addEvents({
20952             /**
20953              * @event show
20954              * Fires when this field show.
20955              * @param {Roo.bootstrap.DateField} thisthis
20956              * @param {Mixed} date The date value
20957              */
20958             show : true,
20959             /**
20960              * @event show
20961              * Fires when this field hide.
20962              * @param {Roo.bootstrap.DateField} this
20963              * @param {Mixed} date The date value
20964              */
20965             hide : true,
20966             /**
20967              * @event select
20968              * Fires when select a date.
20969              * @param {Roo.bootstrap.DateField} this
20970              * @param {Mixed} date The date value
20971              */
20972             select : true
20973         });
20974 };
20975
20976 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20977     
20978     /**
20979      * @cfg {String} format
20980      * The default time format string which can be overriden for localization support.  The format must be
20981      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20982      */
20983     format : "H:i",
20984        
20985     onRender: function(ct, position)
20986     {
20987         
20988         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20989                 
20990         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20991         
20992         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20993         
20994         this.pop = this.picker().select('>.datepicker-time',true).first();
20995         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20996         
20997         this.picker().on('mousedown', this.onMousedown, this);
20998         this.picker().on('click', this.onClick, this);
20999         
21000         this.picker().addClass('datepicker-dropdown');
21001     
21002         this.fillTime();
21003         this.update();
21004             
21005         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21006         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21007         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21008         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21009         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21010         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21011
21012     },
21013     
21014     fireKey: function(e){
21015         if (!this.picker().isVisible()){
21016             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21017                 this.show();
21018             }
21019             return;
21020         }
21021
21022         e.preventDefault();
21023         
21024         switch(e.keyCode){
21025             case 27: // escape
21026                 this.hide();
21027                 break;
21028             case 37: // left
21029             case 39: // right
21030                 this.onTogglePeriod();
21031                 break;
21032             case 38: // up
21033                 this.onIncrementMinutes();
21034                 break;
21035             case 40: // down
21036                 this.onDecrementMinutes();
21037                 break;
21038             case 13: // enter
21039             case 9: // tab
21040                 this.setTime();
21041                 break;
21042         }
21043     },
21044     
21045     onClick: function(e) {
21046         e.stopPropagation();
21047         e.preventDefault();
21048     },
21049     
21050     picker : function()
21051     {
21052         return this.el.select('.datepicker', true).first();
21053     },
21054     
21055     fillTime: function()
21056     {    
21057         var time = this.pop.select('tbody', true).first();
21058         
21059         time.dom.innerHTML = '';
21060         
21061         time.createChild({
21062             tag: 'tr',
21063             cn: [
21064                 {
21065                     tag: 'td',
21066                     cn: [
21067                         {
21068                             tag: 'a',
21069                             href: '#',
21070                             cls: 'btn',
21071                             cn: [
21072                                 {
21073                                     tag: 'span',
21074                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21075                                 }
21076                             ]
21077                         } 
21078                     ]
21079                 },
21080                 {
21081                     tag: 'td',
21082                     cls: 'separator'
21083                 },
21084                 {
21085                     tag: 'td',
21086                     cn: [
21087                         {
21088                             tag: 'a',
21089                             href: '#',
21090                             cls: 'btn',
21091                             cn: [
21092                                 {
21093                                     tag: 'span',
21094                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21095                                 }
21096                             ]
21097                         }
21098                     ]
21099                 },
21100                 {
21101                     tag: 'td',
21102                     cls: 'separator'
21103                 }
21104             ]
21105         });
21106         
21107         time.createChild({
21108             tag: 'tr',
21109             cn: [
21110                 {
21111                     tag: 'td',
21112                     cn: [
21113                         {
21114                             tag: 'span',
21115                             cls: 'timepicker-hour',
21116                             html: '00'
21117                         }  
21118                     ]
21119                 },
21120                 {
21121                     tag: 'td',
21122                     cls: 'separator',
21123                     html: ':'
21124                 },
21125                 {
21126                     tag: 'td',
21127                     cn: [
21128                         {
21129                             tag: 'span',
21130                             cls: 'timepicker-minute',
21131                             html: '00'
21132                         }  
21133                     ]
21134                 },
21135                 {
21136                     tag: 'td',
21137                     cls: 'separator'
21138                 },
21139                 {
21140                     tag: 'td',
21141                     cn: [
21142                         {
21143                             tag: 'button',
21144                             type: 'button',
21145                             cls: 'btn btn-primary period',
21146                             html: 'AM'
21147                             
21148                         }
21149                     ]
21150                 }
21151             ]
21152         });
21153         
21154         time.createChild({
21155             tag: 'tr',
21156             cn: [
21157                 {
21158                     tag: 'td',
21159                     cn: [
21160                         {
21161                             tag: 'a',
21162                             href: '#',
21163                             cls: 'btn',
21164                             cn: [
21165                                 {
21166                                     tag: 'span',
21167                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21168                                 }
21169                             ]
21170                         }
21171                     ]
21172                 },
21173                 {
21174                     tag: 'td',
21175                     cls: 'separator'
21176                 },
21177                 {
21178                     tag: 'td',
21179                     cn: [
21180                         {
21181                             tag: 'a',
21182                             href: '#',
21183                             cls: 'btn',
21184                             cn: [
21185                                 {
21186                                     tag: 'span',
21187                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21188                                 }
21189                             ]
21190                         }
21191                     ]
21192                 },
21193                 {
21194                     tag: 'td',
21195                     cls: 'separator'
21196                 }
21197             ]
21198         });
21199         
21200     },
21201     
21202     update: function()
21203     {
21204         
21205         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21206         
21207         this.fill();
21208     },
21209     
21210     fill: function() 
21211     {
21212         var hours = this.time.getHours();
21213         var minutes = this.time.getMinutes();
21214         var period = 'AM';
21215         
21216         if(hours > 11){
21217             period = 'PM';
21218         }
21219         
21220         if(hours == 0){
21221             hours = 12;
21222         }
21223         
21224         
21225         if(hours > 12){
21226             hours = hours - 12;
21227         }
21228         
21229         if(hours < 10){
21230             hours = '0' + hours;
21231         }
21232         
21233         if(minutes < 10){
21234             minutes = '0' + minutes;
21235         }
21236         
21237         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21238         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21239         this.pop.select('button', true).first().dom.innerHTML = period;
21240         
21241     },
21242     
21243     place: function()
21244     {   
21245         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21246         
21247         var cls = ['bottom'];
21248         
21249         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21250             cls.pop();
21251             cls.push('top');
21252         }
21253         
21254         cls.push('right');
21255         
21256         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21257             cls.pop();
21258             cls.push('left');
21259         }
21260         
21261         this.picker().addClass(cls.join('-'));
21262         
21263         var _this = this;
21264         
21265         Roo.each(cls, function(c){
21266             if(c == 'bottom'){
21267                 _this.picker().setTop(_this.inputEl().getHeight());
21268                 return;
21269             }
21270             if(c == 'top'){
21271                 _this.picker().setTop(0 - _this.picker().getHeight());
21272                 return;
21273             }
21274             
21275             if(c == 'left'){
21276                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21277                 return;
21278             }
21279             if(c == 'right'){
21280                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21281                 return;
21282             }
21283         });
21284         
21285     },
21286   
21287     onFocus : function()
21288     {
21289         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21290         this.show();
21291     },
21292     
21293     onBlur : function()
21294     {
21295         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21296         this.hide();
21297     },
21298     
21299     show : function()
21300     {
21301         this.picker().show();
21302         this.pop.show();
21303         this.update();
21304         this.place();
21305         
21306         this.fireEvent('show', this, this.date);
21307     },
21308     
21309     hide : function()
21310     {
21311         this.picker().hide();
21312         this.pop.hide();
21313         
21314         this.fireEvent('hide', this, this.date);
21315     },
21316     
21317     setTime : function()
21318     {
21319         this.hide();
21320         this.setValue(this.time.format(this.format));
21321         
21322         this.fireEvent('select', this, this.date);
21323         
21324         
21325     },
21326     
21327     onMousedown: function(e){
21328         e.stopPropagation();
21329         e.preventDefault();
21330     },
21331     
21332     onIncrementHours: function()
21333     {
21334         Roo.log('onIncrementHours');
21335         this.time = this.time.add(Date.HOUR, 1);
21336         this.update();
21337         
21338     },
21339     
21340     onDecrementHours: function()
21341     {
21342         Roo.log('onDecrementHours');
21343         this.time = this.time.add(Date.HOUR, -1);
21344         this.update();
21345     },
21346     
21347     onIncrementMinutes: function()
21348     {
21349         Roo.log('onIncrementMinutes');
21350         this.time = this.time.add(Date.MINUTE, 1);
21351         this.update();
21352     },
21353     
21354     onDecrementMinutes: function()
21355     {
21356         Roo.log('onDecrementMinutes');
21357         this.time = this.time.add(Date.MINUTE, -1);
21358         this.update();
21359     },
21360     
21361     onTogglePeriod: function()
21362     {
21363         Roo.log('onTogglePeriod');
21364         this.time = this.time.add(Date.HOUR, 12);
21365         this.update();
21366     }
21367     
21368    
21369 });
21370
21371 Roo.apply(Roo.bootstrap.TimeField,  {
21372     
21373     content : {
21374         tag: 'tbody',
21375         cn: [
21376             {
21377                 tag: 'tr',
21378                 cn: [
21379                 {
21380                     tag: 'td',
21381                     colspan: '7'
21382                 }
21383                 ]
21384             }
21385         ]
21386     },
21387     
21388     footer : {
21389         tag: 'tfoot',
21390         cn: [
21391             {
21392                 tag: 'tr',
21393                 cn: [
21394                 {
21395                     tag: 'th',
21396                     colspan: '7',
21397                     cls: '',
21398                     cn: [
21399                         {
21400                             tag: 'button',
21401                             cls: 'btn btn-info ok',
21402                             html: 'OK'
21403                         }
21404                     ]
21405                 }
21406
21407                 ]
21408             }
21409         ]
21410     }
21411 });
21412
21413 Roo.apply(Roo.bootstrap.TimeField,  {
21414   
21415     template : {
21416         tag: 'div',
21417         cls: 'datepicker dropdown-menu',
21418         cn: [
21419             {
21420                 tag: 'div',
21421                 cls: 'datepicker-time',
21422                 cn: [
21423                 {
21424                     tag: 'table',
21425                     cls: 'table-condensed',
21426                     cn:[
21427                     Roo.bootstrap.TimeField.content,
21428                     Roo.bootstrap.TimeField.footer
21429                     ]
21430                 }
21431                 ]
21432             }
21433         ]
21434     }
21435 });
21436
21437  
21438
21439  /*
21440  * - LGPL
21441  *
21442  * MonthField
21443  * 
21444  */
21445
21446 /**
21447  * @class Roo.bootstrap.MonthField
21448  * @extends Roo.bootstrap.Input
21449  * Bootstrap MonthField class
21450  * 
21451  * @cfg {String} language default en
21452  * 
21453  * @constructor
21454  * Create a new MonthField
21455  * @param {Object} config The config object
21456  */
21457
21458 Roo.bootstrap.MonthField = function(config){
21459     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21460     
21461     this.addEvents({
21462         /**
21463          * @event show
21464          * Fires when this field show.
21465          * @param {Roo.bootstrap.MonthField} this
21466          * @param {Mixed} date The date value
21467          */
21468         show : true,
21469         /**
21470          * @event show
21471          * Fires when this field hide.
21472          * @param {Roo.bootstrap.MonthField} this
21473          * @param {Mixed} date The date value
21474          */
21475         hide : true,
21476         /**
21477          * @event select
21478          * Fires when select a date.
21479          * @param {Roo.bootstrap.MonthField} this
21480          * @param {String} oldvalue The old value
21481          * @param {String} newvalue The new value
21482          */
21483         select : true
21484     });
21485 };
21486
21487 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21488     
21489     onRender: function(ct, position)
21490     {
21491         
21492         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21493         
21494         this.language = this.language || 'en';
21495         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21496         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21497         
21498         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21499         this.isInline = false;
21500         this.isInput = true;
21501         this.component = this.el.select('.add-on', true).first() || false;
21502         this.component = (this.component && this.component.length === 0) ? false : this.component;
21503         this.hasInput = this.component && this.inputEL().length;
21504         
21505         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21506         
21507         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21508         
21509         this.picker().on('mousedown', this.onMousedown, this);
21510         this.picker().on('click', this.onClick, this);
21511         
21512         this.picker().addClass('datepicker-dropdown');
21513         
21514         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21515             v.setStyle('width', '189px');
21516         });
21517         
21518         this.fillMonths();
21519         
21520         this.update();
21521         
21522         if(this.isInline) {
21523             this.show();
21524         }
21525         
21526     },
21527     
21528     setValue: function(v, suppressEvent)
21529     {   
21530         var o = this.getValue();
21531         
21532         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21533         
21534         this.update();
21535
21536         if(suppressEvent !== true){
21537             this.fireEvent('select', this, o, v);
21538         }
21539         
21540     },
21541     
21542     getValue: function()
21543     {
21544         return this.value;
21545     },
21546     
21547     onClick: function(e) 
21548     {
21549         e.stopPropagation();
21550         e.preventDefault();
21551         
21552         var target = e.getTarget();
21553         
21554         if(target.nodeName.toLowerCase() === 'i'){
21555             target = Roo.get(target).dom.parentNode;
21556         }
21557         
21558         var nodeName = target.nodeName;
21559         var className = target.className;
21560         var html = target.innerHTML;
21561         
21562         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21563             return;
21564         }
21565         
21566         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21567         
21568         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21569         
21570         this.hide();
21571                         
21572     },
21573     
21574     picker : function()
21575     {
21576         return this.pickerEl;
21577     },
21578     
21579     fillMonths: function()
21580     {    
21581         var i = 0;
21582         var months = this.picker().select('>.datepicker-months td', true).first();
21583         
21584         months.dom.innerHTML = '';
21585         
21586         while (i < 12) {
21587             var month = {
21588                 tag: 'span',
21589                 cls: 'month',
21590                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21591             };
21592             
21593             months.createChild(month);
21594         }
21595         
21596     },
21597     
21598     update: function()
21599     {
21600         var _this = this;
21601         
21602         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21603             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21604         }
21605         
21606         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21607             e.removeClass('active');
21608             
21609             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21610                 e.addClass('active');
21611             }
21612         })
21613     },
21614     
21615     place: function()
21616     {
21617         if(this.isInline) {
21618             return;
21619         }
21620         
21621         this.picker().removeClass(['bottom', 'top']);
21622         
21623         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21624             /*
21625              * place to the top of element!
21626              *
21627              */
21628             
21629             this.picker().addClass('top');
21630             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21631             
21632             return;
21633         }
21634         
21635         this.picker().addClass('bottom');
21636         
21637         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21638     },
21639     
21640     onFocus : function()
21641     {
21642         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21643         this.show();
21644     },
21645     
21646     onBlur : function()
21647     {
21648         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21649         
21650         var d = this.inputEl().getValue();
21651         
21652         this.setValue(d);
21653                 
21654         this.hide();
21655     },
21656     
21657     show : function()
21658     {
21659         this.picker().show();
21660         this.picker().select('>.datepicker-months', true).first().show();
21661         this.update();
21662         this.place();
21663         
21664         this.fireEvent('show', this, this.date);
21665     },
21666     
21667     hide : function()
21668     {
21669         if(this.isInline) {
21670             return;
21671         }
21672         this.picker().hide();
21673         this.fireEvent('hide', this, this.date);
21674         
21675     },
21676     
21677     onMousedown: function(e)
21678     {
21679         e.stopPropagation();
21680         e.preventDefault();
21681     },
21682     
21683     keyup: function(e)
21684     {
21685         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21686         this.update();
21687     },
21688
21689     fireKey: function(e)
21690     {
21691         if (!this.picker().isVisible()){
21692             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21693                 this.show();
21694             }
21695             return;
21696         }
21697         
21698         var dir;
21699         
21700         switch(e.keyCode){
21701             case 27: // escape
21702                 this.hide();
21703                 e.preventDefault();
21704                 break;
21705             case 37: // left
21706             case 39: // right
21707                 dir = e.keyCode == 37 ? -1 : 1;
21708                 
21709                 this.vIndex = this.vIndex + dir;
21710                 
21711                 if(this.vIndex < 0){
21712                     this.vIndex = 0;
21713                 }
21714                 
21715                 if(this.vIndex > 11){
21716                     this.vIndex = 11;
21717                 }
21718                 
21719                 if(isNaN(this.vIndex)){
21720                     this.vIndex = 0;
21721                 }
21722                 
21723                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21724                 
21725                 break;
21726             case 38: // up
21727             case 40: // down
21728                 
21729                 dir = e.keyCode == 38 ? -1 : 1;
21730                 
21731                 this.vIndex = this.vIndex + dir * 4;
21732                 
21733                 if(this.vIndex < 0){
21734                     this.vIndex = 0;
21735                 }
21736                 
21737                 if(this.vIndex > 11){
21738                     this.vIndex = 11;
21739                 }
21740                 
21741                 if(isNaN(this.vIndex)){
21742                     this.vIndex = 0;
21743                 }
21744                 
21745                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21746                 break;
21747                 
21748             case 13: // enter
21749                 
21750                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21751                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21752                 }
21753                 
21754                 this.hide();
21755                 e.preventDefault();
21756                 break;
21757             case 9: // tab
21758                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21759                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21760                 }
21761                 this.hide();
21762                 break;
21763             case 16: // shift
21764             case 17: // ctrl
21765             case 18: // alt
21766                 break;
21767             default :
21768                 this.hide();
21769                 
21770         }
21771     },
21772     
21773     remove: function() 
21774     {
21775         this.picker().remove();
21776     }
21777    
21778 });
21779
21780 Roo.apply(Roo.bootstrap.MonthField,  {
21781     
21782     content : {
21783         tag: 'tbody',
21784         cn: [
21785         {
21786             tag: 'tr',
21787             cn: [
21788             {
21789                 tag: 'td',
21790                 colspan: '7'
21791             }
21792             ]
21793         }
21794         ]
21795     },
21796     
21797     dates:{
21798         en: {
21799             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21800             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21801         }
21802     }
21803 });
21804
21805 Roo.apply(Roo.bootstrap.MonthField,  {
21806   
21807     template : {
21808         tag: 'div',
21809         cls: 'datepicker dropdown-menu roo-dynamic',
21810         cn: [
21811             {
21812                 tag: 'div',
21813                 cls: 'datepicker-months',
21814                 cn: [
21815                 {
21816                     tag: 'table',
21817                     cls: 'table-condensed',
21818                     cn:[
21819                         Roo.bootstrap.DateField.content
21820                     ]
21821                 }
21822                 ]
21823             }
21824         ]
21825     }
21826 });
21827
21828  
21829
21830  
21831  /*
21832  * - LGPL
21833  *
21834  * CheckBox
21835  * 
21836  */
21837
21838 /**
21839  * @class Roo.bootstrap.CheckBox
21840  * @extends Roo.bootstrap.Input
21841  * Bootstrap CheckBox class
21842  * 
21843  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21844  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21845  * @cfg {String} boxLabel The text that appears beside the checkbox
21846  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21847  * @cfg {Boolean} checked initnal the element
21848  * @cfg {Boolean} inline inline the element (default false)
21849  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21850  * @cfg {String} tooltip label tooltip
21851  * 
21852  * @constructor
21853  * Create a new CheckBox
21854  * @param {Object} config The config object
21855  */
21856
21857 Roo.bootstrap.CheckBox = function(config){
21858     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21859    
21860     this.addEvents({
21861         /**
21862         * @event check
21863         * Fires when the element is checked or unchecked.
21864         * @param {Roo.bootstrap.CheckBox} this This input
21865         * @param {Boolean} checked The new checked value
21866         */
21867        check : true,
21868        /**
21869         * @event click
21870         * Fires when the element is click.
21871         * @param {Roo.bootstrap.CheckBox} this This input
21872         */
21873        click : true
21874     });
21875     
21876 };
21877
21878 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21879   
21880     inputType: 'checkbox',
21881     inputValue: 1,
21882     valueOff: 0,
21883     boxLabel: false,
21884     checked: false,
21885     weight : false,
21886     inline: false,
21887     tooltip : '',
21888     
21889     // checkbox success does not make any sense really.. 
21890     invalidClass : "",
21891     validClass : "",
21892     
21893     
21894     getAutoCreate : function()
21895     {
21896         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21897         
21898         var id = Roo.id();
21899         
21900         var cfg = {};
21901         
21902         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21903         
21904         if(this.inline){
21905             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21906         }
21907         
21908         var input =  {
21909             tag: 'input',
21910             id : id,
21911             type : this.inputType,
21912             value : this.inputValue,
21913             cls : 'roo-' + this.inputType, //'form-box',
21914             placeholder : this.placeholder || ''
21915             
21916         };
21917         
21918         if(this.inputType != 'radio'){
21919             var hidden =  {
21920                 tag: 'input',
21921                 type : 'hidden',
21922                 cls : 'roo-hidden-value',
21923                 value : this.checked ? this.inputValue : this.valueOff
21924             };
21925         }
21926         
21927             
21928         if (this.weight) { // Validity check?
21929             cfg.cls += " " + this.inputType + "-" + this.weight;
21930         }
21931         
21932         if (this.disabled) {
21933             input.disabled=true;
21934         }
21935         
21936         if(this.checked){
21937             input.checked = this.checked;
21938         }
21939         
21940         if (this.name) {
21941             
21942             input.name = this.name;
21943             
21944             if(this.inputType != 'radio'){
21945                 hidden.name = this.name;
21946                 input.name = '_hidden_' + this.name;
21947             }
21948         }
21949         
21950         if (this.size) {
21951             input.cls += ' input-' + this.size;
21952         }
21953         
21954         var settings=this;
21955         
21956         ['xs','sm','md','lg'].map(function(size){
21957             if (settings[size]) {
21958                 cfg.cls += ' col-' + size + '-' + settings[size];
21959             }
21960         });
21961         
21962         var inputblock = input;
21963          
21964         if (this.before || this.after) {
21965             
21966             inputblock = {
21967                 cls : 'input-group',
21968                 cn :  [] 
21969             };
21970             
21971             if (this.before) {
21972                 inputblock.cn.push({
21973                     tag :'span',
21974                     cls : 'input-group-addon',
21975                     html : this.before
21976                 });
21977             }
21978             
21979             inputblock.cn.push(input);
21980             
21981             if(this.inputType != 'radio'){
21982                 inputblock.cn.push(hidden);
21983             }
21984             
21985             if (this.after) {
21986                 inputblock.cn.push({
21987                     tag :'span',
21988                     cls : 'input-group-addon',
21989                     html : this.after
21990                 });
21991             }
21992             
21993         }
21994         var boxLabelCfg = false;
21995         
21996         if(this.boxLabel){
21997            
21998             boxLabelCfg = {
21999                 tag: 'label',
22000                 //'for': id, // box label is handled by onclick - so no for...
22001                 cls: 'box-label',
22002                 html: this.boxLabel
22003             };
22004             if(this.tooltip){
22005                 boxLabelCfg.tooltip = this.tooltip;
22006             }
22007              
22008         }
22009         
22010         
22011         if (align ==='left' && this.fieldLabel.length) {
22012 //                Roo.log("left and has label");
22013             cfg.cn = [
22014                 {
22015                     tag: 'label',
22016                     'for' :  id,
22017                     cls : 'control-label',
22018                     html : this.fieldLabel
22019                 },
22020                 {
22021                     cls : "", 
22022                     cn: [
22023                         inputblock
22024                     ]
22025                 }
22026             ];
22027             
22028             if (boxLabelCfg) {
22029                 cfg.cn[1].cn.push(boxLabelCfg);
22030             }
22031             
22032             if(this.labelWidth > 12){
22033                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22034             }
22035             
22036             if(this.labelWidth < 13 && this.labelmd == 0){
22037                 this.labelmd = this.labelWidth;
22038             }
22039             
22040             if(this.labellg > 0){
22041                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22042                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22043             }
22044             
22045             if(this.labelmd > 0){
22046                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22047                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22048             }
22049             
22050             if(this.labelsm > 0){
22051                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22052                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22053             }
22054             
22055             if(this.labelxs > 0){
22056                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22057                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22058             }
22059             
22060         } else if ( this.fieldLabel.length) {
22061 //                Roo.log(" label");
22062                 cfg.cn = [
22063                    
22064                     {
22065                         tag: this.boxLabel ? 'span' : 'label',
22066                         'for': id,
22067                         cls: 'control-label box-input-label',
22068                         //cls : 'input-group-addon',
22069                         html : this.fieldLabel
22070                     },
22071                     
22072                     inputblock
22073                     
22074                 ];
22075                 if (boxLabelCfg) {
22076                     cfg.cn.push(boxLabelCfg);
22077                 }
22078
22079         } else {
22080             
22081 //                Roo.log(" no label && no align");
22082                 cfg.cn = [  inputblock ] ;
22083                 if (boxLabelCfg) {
22084                     cfg.cn.push(boxLabelCfg);
22085                 }
22086
22087                 
22088         }
22089         
22090        
22091         
22092         if(this.inputType != 'radio'){
22093             cfg.cn.push(hidden);
22094         }
22095         
22096         return cfg;
22097         
22098     },
22099     
22100     /**
22101      * return the real input element.
22102      */
22103     inputEl: function ()
22104     {
22105         return this.el.select('input.roo-' + this.inputType,true).first();
22106     },
22107     hiddenEl: function ()
22108     {
22109         return this.el.select('input.roo-hidden-value',true).first();
22110     },
22111     
22112     labelEl: function()
22113     {
22114         return this.el.select('label.control-label',true).first();
22115     },
22116     /* depricated... */
22117     
22118     label: function()
22119     {
22120         return this.labelEl();
22121     },
22122     
22123     boxLabelEl: function()
22124     {
22125         return this.el.select('label.box-label',true).first();
22126     },
22127     
22128     initEvents : function()
22129     {
22130 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22131         
22132         this.inputEl().on('click', this.onClick,  this);
22133         
22134         if (this.boxLabel) { 
22135             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22136         }
22137         
22138         this.startValue = this.getValue();
22139         
22140         if(this.groupId){
22141             Roo.bootstrap.CheckBox.register(this);
22142         }
22143     },
22144     
22145     onClick : function(e)
22146     {   
22147         if(this.fireEvent('click', this, e) !== false){
22148             this.setChecked(!this.checked);
22149         }
22150         
22151     },
22152     
22153     setChecked : function(state,suppressEvent)
22154     {
22155         this.startValue = this.getValue();
22156
22157         if(this.inputType == 'radio'){
22158             
22159             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22160                 e.dom.checked = false;
22161             });
22162             
22163             this.inputEl().dom.checked = true;
22164             
22165             this.inputEl().dom.value = this.inputValue;
22166             
22167             if(suppressEvent !== true){
22168                 this.fireEvent('check', this, true);
22169             }
22170             
22171             this.validate();
22172             
22173             return;
22174         }
22175         
22176         this.checked = state;
22177         
22178         this.inputEl().dom.checked = state;
22179         
22180         
22181         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22182         
22183         if(suppressEvent !== true){
22184             this.fireEvent('check', this, state);
22185         }
22186         
22187         this.validate();
22188     },
22189     
22190     getValue : function()
22191     {
22192         if(this.inputType == 'radio'){
22193             return this.getGroupValue();
22194         }
22195         
22196         return this.hiddenEl().dom.value;
22197         
22198     },
22199     
22200     getGroupValue : function()
22201     {
22202         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22203             return '';
22204         }
22205         
22206         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22207     },
22208     
22209     setValue : function(v,suppressEvent)
22210     {
22211         if(this.inputType == 'radio'){
22212             this.setGroupValue(v, suppressEvent);
22213             return;
22214         }
22215         
22216         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22217         
22218         this.validate();
22219     },
22220     
22221     setGroupValue : function(v, suppressEvent)
22222     {
22223         this.startValue = this.getValue();
22224         
22225         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22226             e.dom.checked = false;
22227             
22228             if(e.dom.value == v){
22229                 e.dom.checked = true;
22230             }
22231         });
22232         
22233         if(suppressEvent !== true){
22234             this.fireEvent('check', this, true);
22235         }
22236
22237         this.validate();
22238         
22239         return;
22240     },
22241     
22242     validate : function()
22243     {
22244         if(this.getVisibilityEl().hasClass('hidden')){
22245             return true;
22246         }
22247         
22248         if(
22249                 this.disabled || 
22250                 (this.inputType == 'radio' && this.validateRadio()) ||
22251                 (this.inputType == 'checkbox' && this.validateCheckbox())
22252         ){
22253             this.markValid();
22254             return true;
22255         }
22256         
22257         this.markInvalid();
22258         return false;
22259     },
22260     
22261     validateRadio : function()
22262     {
22263         if(this.getVisibilityEl().hasClass('hidden')){
22264             return true;
22265         }
22266         
22267         if(this.allowBlank){
22268             return true;
22269         }
22270         
22271         var valid = false;
22272         
22273         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22274             if(!e.dom.checked){
22275                 return;
22276             }
22277             
22278             valid = true;
22279             
22280             return false;
22281         });
22282         
22283         return valid;
22284     },
22285     
22286     validateCheckbox : function()
22287     {
22288         if(!this.groupId){
22289             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22290             //return (this.getValue() == this.inputValue) ? true : false;
22291         }
22292         
22293         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22294         
22295         if(!group){
22296             return false;
22297         }
22298         
22299         var r = false;
22300         
22301         for(var i in group){
22302             if(group[i].el.isVisible(true)){
22303                 r = false;
22304                 break;
22305             }
22306             
22307             r = true;
22308         }
22309         
22310         for(var i in group){
22311             if(r){
22312                 break;
22313             }
22314             
22315             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22316         }
22317         
22318         return r;
22319     },
22320     
22321     /**
22322      * Mark this field as valid
22323      */
22324     markValid : function()
22325     {
22326         var _this = this;
22327         
22328         this.fireEvent('valid', this);
22329         
22330         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22331         
22332         if(this.groupId){
22333             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22334         }
22335         
22336         if(label){
22337             label.markValid();
22338         }
22339
22340         if(this.inputType == 'radio'){
22341             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22342                 var fg = e.findParent('.form-group', false, true);
22343                 if (Roo.bootstrap.version == 3) {
22344                     fg.removeClass([_this.invalidClass, _this.validClass]);
22345                     fg.addClass(_this.validClass);
22346                 } else {
22347                     fg.removeClass(['is-valid', 'is-invalid']);
22348                     fg.addClass('is-valid');
22349                 }
22350             });
22351             
22352             return;
22353         }
22354
22355         if(!this.groupId){
22356             var fg = this.el.findParent('.form-group', false, true);
22357             if (Roo.bootstrap.version == 3) {
22358                 fg.removeClass([this.invalidClass, this.validClass]);
22359                 fg.addClass(this.validClass);
22360             } else {
22361                 fg.removeClass(['is-valid', 'is-invalid']);
22362                 fg.addClass('is-valid');
22363             }
22364             return;
22365         }
22366         
22367         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22368         
22369         if(!group){
22370             return;
22371         }
22372         
22373         for(var i in group){
22374             var fg = group[i].el.findParent('.form-group', false, true);
22375             if (Roo.bootstrap.version == 3) {
22376                 fg.removeClass([this.invalidClass, this.validClass]);
22377                 fg.addClass(this.validClass);
22378             } else {
22379                 fg.removeClass(['is-valid', 'is-invalid']);
22380                 fg.addClass('is-valid');
22381             }
22382         }
22383     },
22384     
22385      /**
22386      * Mark this field as invalid
22387      * @param {String} msg The validation message
22388      */
22389     markInvalid : function(msg)
22390     {
22391         if(this.allowBlank){
22392             return;
22393         }
22394         
22395         var _this = this;
22396         
22397         this.fireEvent('invalid', this, msg);
22398         
22399         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22400         
22401         if(this.groupId){
22402             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22403         }
22404         
22405         if(label){
22406             label.markInvalid();
22407         }
22408             
22409         if(this.inputType == 'radio'){
22410             
22411             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22412                 var fg = e.findParent('.form-group', false, true);
22413                 if (Roo.bootstrap.version == 3) {
22414                     fg.removeClass([_this.invalidClass, _this.validClass]);
22415                     fg.addClass(_this.invalidClass);
22416                 } else {
22417                     fg.removeClass(['is-invalid', 'is-valid']);
22418                     fg.addClass('is-invalid');
22419                 }
22420             });
22421             
22422             return;
22423         }
22424         
22425         if(!this.groupId){
22426             var fg = this.el.findParent('.form-group', false, true);
22427             if (Roo.bootstrap.version == 3) {
22428                 fg.removeClass([_this.invalidClass, _this.validClass]);
22429                 fg.addClass(_this.invalidClass);
22430             } else {
22431                 fg.removeClass(['is-invalid', 'is-valid']);
22432                 fg.addClass('is-invalid');
22433             }
22434             return;
22435         }
22436         
22437         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22438         
22439         if(!group){
22440             return;
22441         }
22442         
22443         for(var i in group){
22444             var fg = group[i].el.findParent('.form-group', false, true);
22445             if (Roo.bootstrap.version == 3) {
22446                 fg.removeClass([_this.invalidClass, _this.validClass]);
22447                 fg.addClass(_this.invalidClass);
22448             } else {
22449                 fg.removeClass(['is-invalid', 'is-valid']);
22450                 fg.addClass('is-invalid');
22451             }
22452         }
22453         
22454     },
22455     
22456     clearInvalid : function()
22457     {
22458         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22459         
22460         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22461         
22462         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22463         
22464         if (label && label.iconEl) {
22465             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22466             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22467         }
22468     },
22469     
22470     disable : function()
22471     {
22472         if(this.inputType != 'radio'){
22473             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22474             return;
22475         }
22476         
22477         var _this = this;
22478         
22479         if(this.rendered){
22480             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22481                 _this.getActionEl().addClass(this.disabledClass);
22482                 e.dom.disabled = true;
22483             });
22484         }
22485         
22486         this.disabled = true;
22487         this.fireEvent("disable", this);
22488         return this;
22489     },
22490
22491     enable : function()
22492     {
22493         if(this.inputType != 'radio'){
22494             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22495             return;
22496         }
22497         
22498         var _this = this;
22499         
22500         if(this.rendered){
22501             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22502                 _this.getActionEl().removeClass(this.disabledClass);
22503                 e.dom.disabled = false;
22504             });
22505         }
22506         
22507         this.disabled = false;
22508         this.fireEvent("enable", this);
22509         return this;
22510     },
22511     
22512     setBoxLabel : function(v)
22513     {
22514         this.boxLabel = v;
22515         
22516         if(this.rendered){
22517             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22518         }
22519     }
22520
22521 });
22522
22523 Roo.apply(Roo.bootstrap.CheckBox, {
22524     
22525     groups: {},
22526     
22527      /**
22528     * register a CheckBox Group
22529     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22530     */
22531     register : function(checkbox)
22532     {
22533         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22534             this.groups[checkbox.groupId] = {};
22535         }
22536         
22537         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22538             return;
22539         }
22540         
22541         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22542         
22543     },
22544     /**
22545     * fetch a CheckBox Group based on the group ID
22546     * @param {string} the group ID
22547     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22548     */
22549     get: function(groupId) {
22550         if (typeof(this.groups[groupId]) == 'undefined') {
22551             return false;
22552         }
22553         
22554         return this.groups[groupId] ;
22555     }
22556     
22557     
22558 });
22559 /*
22560  * - LGPL
22561  *
22562  * RadioItem
22563  * 
22564  */
22565
22566 /**
22567  * @class Roo.bootstrap.Radio
22568  * @extends Roo.bootstrap.Component
22569  * Bootstrap Radio class
22570  * @cfg {String} boxLabel - the label associated
22571  * @cfg {String} value - the value of radio
22572  * 
22573  * @constructor
22574  * Create a new Radio
22575  * @param {Object} config The config object
22576  */
22577 Roo.bootstrap.Radio = function(config){
22578     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22579     
22580 };
22581
22582 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22583     
22584     boxLabel : '',
22585     
22586     value : '',
22587     
22588     getAutoCreate : function()
22589     {
22590         var cfg = {
22591             tag : 'div',
22592             cls : 'form-group radio',
22593             cn : [
22594                 {
22595                     tag : 'label',
22596                     cls : 'box-label',
22597                     html : this.boxLabel
22598                 }
22599             ]
22600         };
22601         
22602         return cfg;
22603     },
22604     
22605     initEvents : function() 
22606     {
22607         this.parent().register(this);
22608         
22609         this.el.on('click', this.onClick, this);
22610         
22611     },
22612     
22613     onClick : function(e)
22614     {
22615         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22616             this.setChecked(true);
22617         }
22618     },
22619     
22620     setChecked : function(state, suppressEvent)
22621     {
22622         this.parent().setValue(this.value, suppressEvent);
22623         
22624     },
22625     
22626     setBoxLabel : function(v)
22627     {
22628         this.boxLabel = v;
22629         
22630         if(this.rendered){
22631             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22632         }
22633     }
22634     
22635 });
22636  
22637
22638  /*
22639  * - LGPL
22640  *
22641  * Input
22642  * 
22643  */
22644
22645 /**
22646  * @class Roo.bootstrap.SecurePass
22647  * @extends Roo.bootstrap.Input
22648  * Bootstrap SecurePass class
22649  *
22650  * 
22651  * @constructor
22652  * Create a new SecurePass
22653  * @param {Object} config The config object
22654  */
22655  
22656 Roo.bootstrap.SecurePass = function (config) {
22657     // these go here, so the translation tool can replace them..
22658     this.errors = {
22659         PwdEmpty: "Please type a password, and then retype it to confirm.",
22660         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22661         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22662         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22663         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22664         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22665         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22666         TooWeak: "Your password is Too Weak."
22667     },
22668     this.meterLabel = "Password strength:";
22669     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22670     this.meterClass = [
22671         "roo-password-meter-tooweak", 
22672         "roo-password-meter-weak", 
22673         "roo-password-meter-medium", 
22674         "roo-password-meter-strong", 
22675         "roo-password-meter-grey"
22676     ];
22677     
22678     this.errors = {};
22679     
22680     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22681 }
22682
22683 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22684     /**
22685      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22686      * {
22687      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22688      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22689      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22690      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22691      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22692      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22693      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22694      * })
22695      */
22696     // private
22697     
22698     meterWidth: 300,
22699     errorMsg :'',    
22700     errors: false,
22701     imageRoot: '/',
22702     /**
22703      * @cfg {String/Object} Label for the strength meter (defaults to
22704      * 'Password strength:')
22705      */
22706     // private
22707     meterLabel: '',
22708     /**
22709      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22710      * ['Weak', 'Medium', 'Strong'])
22711      */
22712     // private    
22713     pwdStrengths: false,    
22714     // private
22715     strength: 0,
22716     // private
22717     _lastPwd: null,
22718     // private
22719     kCapitalLetter: 0,
22720     kSmallLetter: 1,
22721     kDigit: 2,
22722     kPunctuation: 3,
22723     
22724     insecure: false,
22725     // private
22726     initEvents: function ()
22727     {
22728         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22729
22730         if (this.el.is('input[type=password]') && Roo.isSafari) {
22731             this.el.on('keydown', this.SafariOnKeyDown, this);
22732         }
22733
22734         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22735     },
22736     // private
22737     onRender: function (ct, position)
22738     {
22739         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22740         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22741         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22742
22743         this.trigger.createChild({
22744                    cn: [
22745                     {
22746                     //id: 'PwdMeter',
22747                     tag: 'div',
22748                     cls: 'roo-password-meter-grey col-xs-12',
22749                     style: {
22750                         //width: 0,
22751                         //width: this.meterWidth + 'px'                                                
22752                         }
22753                     },
22754                     {                            
22755                          cls: 'roo-password-meter-text'                          
22756                     }
22757                 ]            
22758         });
22759
22760          
22761         if (this.hideTrigger) {
22762             this.trigger.setDisplayed(false);
22763         }
22764         this.setSize(this.width || '', this.height || '');
22765     },
22766     // private
22767     onDestroy: function ()
22768     {
22769         if (this.trigger) {
22770             this.trigger.removeAllListeners();
22771             this.trigger.remove();
22772         }
22773         if (this.wrap) {
22774             this.wrap.remove();
22775         }
22776         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22777     },
22778     // private
22779     checkStrength: function ()
22780     {
22781         var pwd = this.inputEl().getValue();
22782         if (pwd == this._lastPwd) {
22783             return;
22784         }
22785
22786         var strength;
22787         if (this.ClientSideStrongPassword(pwd)) {
22788             strength = 3;
22789         } else if (this.ClientSideMediumPassword(pwd)) {
22790             strength = 2;
22791         } else if (this.ClientSideWeakPassword(pwd)) {
22792             strength = 1;
22793         } else {
22794             strength = 0;
22795         }
22796         
22797         Roo.log('strength1: ' + strength);
22798         
22799         //var pm = this.trigger.child('div/div/div').dom;
22800         var pm = this.trigger.child('div/div');
22801         pm.removeClass(this.meterClass);
22802         pm.addClass(this.meterClass[strength]);
22803                 
22804         
22805         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22806                 
22807         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22808         
22809         this._lastPwd = pwd;
22810     },
22811     reset: function ()
22812     {
22813         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22814         
22815         this._lastPwd = '';
22816         
22817         var pm = this.trigger.child('div/div');
22818         pm.removeClass(this.meterClass);
22819         pm.addClass('roo-password-meter-grey');        
22820         
22821         
22822         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22823         
22824         pt.innerHTML = '';
22825         this.inputEl().dom.type='password';
22826     },
22827     // private
22828     validateValue: function (value)
22829     {
22830         
22831         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22832             return false;
22833         }
22834         if (value.length == 0) {
22835             if (this.allowBlank) {
22836                 this.clearInvalid();
22837                 return true;
22838             }
22839
22840             this.markInvalid(this.errors.PwdEmpty);
22841             this.errorMsg = this.errors.PwdEmpty;
22842             return false;
22843         }
22844         
22845         if(this.insecure){
22846             return true;
22847         }
22848         
22849         if ('[\x21-\x7e]*'.match(value)) {
22850             this.markInvalid(this.errors.PwdBadChar);
22851             this.errorMsg = this.errors.PwdBadChar;
22852             return false;
22853         }
22854         if (value.length < 6) {
22855             this.markInvalid(this.errors.PwdShort);
22856             this.errorMsg = this.errors.PwdShort;
22857             return false;
22858         }
22859         if (value.length > 16) {
22860             this.markInvalid(this.errors.PwdLong);
22861             this.errorMsg = this.errors.PwdLong;
22862             return false;
22863         }
22864         var strength;
22865         if (this.ClientSideStrongPassword(value)) {
22866             strength = 3;
22867         } else if (this.ClientSideMediumPassword(value)) {
22868             strength = 2;
22869         } else if (this.ClientSideWeakPassword(value)) {
22870             strength = 1;
22871         } else {
22872             strength = 0;
22873         }
22874
22875         
22876         if (strength < 2) {
22877             //this.markInvalid(this.errors.TooWeak);
22878             this.errorMsg = this.errors.TooWeak;
22879             //return false;
22880         }
22881         
22882         
22883         console.log('strength2: ' + strength);
22884         
22885         //var pm = this.trigger.child('div/div/div').dom;
22886         
22887         var pm = this.trigger.child('div/div');
22888         pm.removeClass(this.meterClass);
22889         pm.addClass(this.meterClass[strength]);
22890                 
22891         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22892                 
22893         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22894         
22895         this.errorMsg = ''; 
22896         return true;
22897     },
22898     // private
22899     CharacterSetChecks: function (type)
22900     {
22901         this.type = type;
22902         this.fResult = false;
22903     },
22904     // private
22905     isctype: function (character, type)
22906     {
22907         switch (type) {  
22908             case this.kCapitalLetter:
22909                 if (character >= 'A' && character <= 'Z') {
22910                     return true;
22911                 }
22912                 break;
22913             
22914             case this.kSmallLetter:
22915                 if (character >= 'a' && character <= 'z') {
22916                     return true;
22917                 }
22918                 break;
22919             
22920             case this.kDigit:
22921                 if (character >= '0' && character <= '9') {
22922                     return true;
22923                 }
22924                 break;
22925             
22926             case this.kPunctuation:
22927                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22928                     return true;
22929                 }
22930                 break;
22931             
22932             default:
22933                 return false;
22934         }
22935
22936     },
22937     // private
22938     IsLongEnough: function (pwd, size)
22939     {
22940         return !(pwd == null || isNaN(size) || pwd.length < size);
22941     },
22942     // private
22943     SpansEnoughCharacterSets: function (word, nb)
22944     {
22945         if (!this.IsLongEnough(word, nb))
22946         {
22947             return false;
22948         }
22949
22950         var characterSetChecks = new Array(
22951             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22952             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22953         );
22954         
22955         for (var index = 0; index < word.length; ++index) {
22956             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22957                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22958                     characterSetChecks[nCharSet].fResult = true;
22959                     break;
22960                 }
22961             }
22962         }
22963
22964         var nCharSets = 0;
22965         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22966             if (characterSetChecks[nCharSet].fResult) {
22967                 ++nCharSets;
22968             }
22969         }
22970
22971         if (nCharSets < nb) {
22972             return false;
22973         }
22974         return true;
22975     },
22976     // private
22977     ClientSideStrongPassword: function (pwd)
22978     {
22979         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22980     },
22981     // private
22982     ClientSideMediumPassword: function (pwd)
22983     {
22984         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22985     },
22986     // private
22987     ClientSideWeakPassword: function (pwd)
22988     {
22989         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22990     }
22991           
22992 })//<script type="text/javascript">
22993
22994 /*
22995  * Based  Ext JS Library 1.1.1
22996  * Copyright(c) 2006-2007, Ext JS, LLC.
22997  * LGPL
22998  *
22999  */
23000  
23001 /**
23002  * @class Roo.HtmlEditorCore
23003  * @extends Roo.Component
23004  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23005  *
23006  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23007  */
23008
23009 Roo.HtmlEditorCore = function(config){
23010     
23011     
23012     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23013     
23014     
23015     this.addEvents({
23016         /**
23017          * @event initialize
23018          * Fires when the editor is fully initialized (including the iframe)
23019          * @param {Roo.HtmlEditorCore} this
23020          */
23021         initialize: true,
23022         /**
23023          * @event activate
23024          * Fires when the editor is first receives the focus. Any insertion must wait
23025          * until after this event.
23026          * @param {Roo.HtmlEditorCore} this
23027          */
23028         activate: true,
23029          /**
23030          * @event beforesync
23031          * Fires before the textarea is updated with content from the editor iframe. Return false
23032          * to cancel the sync.
23033          * @param {Roo.HtmlEditorCore} this
23034          * @param {String} html
23035          */
23036         beforesync: true,
23037          /**
23038          * @event beforepush
23039          * Fires before the iframe editor is updated with content from the textarea. Return false
23040          * to cancel the push.
23041          * @param {Roo.HtmlEditorCore} this
23042          * @param {String} html
23043          */
23044         beforepush: true,
23045          /**
23046          * @event sync
23047          * Fires when the textarea is updated with content from the editor iframe.
23048          * @param {Roo.HtmlEditorCore} this
23049          * @param {String} html
23050          */
23051         sync: true,
23052          /**
23053          * @event push
23054          * Fires when the iframe editor is updated with content from the textarea.
23055          * @param {Roo.HtmlEditorCore} this
23056          * @param {String} html
23057          */
23058         push: true,
23059         
23060         /**
23061          * @event editorevent
23062          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23063          * @param {Roo.HtmlEditorCore} this
23064          */
23065         editorevent: true
23066         
23067     });
23068     
23069     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23070     
23071     // defaults : white / black...
23072     this.applyBlacklists();
23073     
23074     
23075     
23076 };
23077
23078
23079 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23080
23081
23082      /**
23083      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23084      */
23085     
23086     owner : false,
23087     
23088      /**
23089      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23090      *                        Roo.resizable.
23091      */
23092     resizable : false,
23093      /**
23094      * @cfg {Number} height (in pixels)
23095      */   
23096     height: 300,
23097    /**
23098      * @cfg {Number} width (in pixels)
23099      */   
23100     width: 500,
23101     
23102     /**
23103      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23104      * 
23105      */
23106     stylesheets: false,
23107     
23108     // id of frame..
23109     frameId: false,
23110     
23111     // private properties
23112     validationEvent : false,
23113     deferHeight: true,
23114     initialized : false,
23115     activated : false,
23116     sourceEditMode : false,
23117     onFocus : Roo.emptyFn,
23118     iframePad:3,
23119     hideMode:'offsets',
23120     
23121     clearUp: true,
23122     
23123     // blacklist + whitelisted elements..
23124     black: false,
23125     white: false,
23126      
23127     bodyCls : '',
23128
23129     /**
23130      * Protected method that will not generally be called directly. It
23131      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23132      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23133      */
23134     getDocMarkup : function(){
23135         // body styles..
23136         var st = '';
23137         
23138         // inherit styels from page...?? 
23139         if (this.stylesheets === false) {
23140             
23141             Roo.get(document.head).select('style').each(function(node) {
23142                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23143             });
23144             
23145             Roo.get(document.head).select('link').each(function(node) { 
23146                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23147             });
23148             
23149         } else if (!this.stylesheets.length) {
23150                 // simple..
23151                 st = '<style type="text/css">' +
23152                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23153                    '</style>';
23154         } else { 
23155             st = '<style type="text/css">' +
23156                     this.stylesheets +
23157                 '</style>';
23158         }
23159         
23160         st +=  '<style type="text/css">' +
23161             'IMG { cursor: pointer } ' +
23162         '</style>';
23163
23164         var cls = 'roo-htmleditor-body';
23165         
23166         if(this.bodyCls.length){
23167             cls += ' ' + this.bodyCls;
23168         }
23169         
23170         return '<html><head>' + st  +
23171             //<style type="text/css">' +
23172             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23173             //'</style>' +
23174             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23175     },
23176
23177     // private
23178     onRender : function(ct, position)
23179     {
23180         var _t = this;
23181         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23182         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23183         
23184         
23185         this.el.dom.style.border = '0 none';
23186         this.el.dom.setAttribute('tabIndex', -1);
23187         this.el.addClass('x-hidden hide');
23188         
23189         
23190         
23191         if(Roo.isIE){ // fix IE 1px bogus margin
23192             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23193         }
23194        
23195         
23196         this.frameId = Roo.id();
23197         
23198          
23199         
23200         var iframe = this.owner.wrap.createChild({
23201             tag: 'iframe',
23202             cls: 'form-control', // bootstrap..
23203             id: this.frameId,
23204             name: this.frameId,
23205             frameBorder : 'no',
23206             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23207         }, this.el
23208         );
23209         
23210         
23211         this.iframe = iframe.dom;
23212
23213          this.assignDocWin();
23214         
23215         this.doc.designMode = 'on';
23216        
23217         this.doc.open();
23218         this.doc.write(this.getDocMarkup());
23219         this.doc.close();
23220
23221         
23222         var task = { // must defer to wait for browser to be ready
23223             run : function(){
23224                 //console.log("run task?" + this.doc.readyState);
23225                 this.assignDocWin();
23226                 if(this.doc.body || this.doc.readyState == 'complete'){
23227                     try {
23228                         this.doc.designMode="on";
23229                     } catch (e) {
23230                         return;
23231                     }
23232                     Roo.TaskMgr.stop(task);
23233                     this.initEditor.defer(10, this);
23234                 }
23235             },
23236             interval : 10,
23237             duration: 10000,
23238             scope: this
23239         };
23240         Roo.TaskMgr.start(task);
23241
23242     },
23243
23244     // private
23245     onResize : function(w, h)
23246     {
23247          Roo.log('resize: ' +w + ',' + h );
23248         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23249         if(!this.iframe){
23250             return;
23251         }
23252         if(typeof w == 'number'){
23253             
23254             this.iframe.style.width = w + 'px';
23255         }
23256         if(typeof h == 'number'){
23257             
23258             this.iframe.style.height = h + 'px';
23259             if(this.doc){
23260                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23261             }
23262         }
23263         
23264     },
23265
23266     /**
23267      * Toggles the editor between standard and source edit mode.
23268      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23269      */
23270     toggleSourceEdit : function(sourceEditMode){
23271         
23272         this.sourceEditMode = sourceEditMode === true;
23273         
23274         if(this.sourceEditMode){
23275  
23276             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23277             
23278         }else{
23279             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23280             //this.iframe.className = '';
23281             this.deferFocus();
23282         }
23283         //this.setSize(this.owner.wrap.getSize());
23284         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23285     },
23286
23287     
23288   
23289
23290     /**
23291      * Protected method that will not generally be called directly. If you need/want
23292      * custom HTML cleanup, this is the method you should override.
23293      * @param {String} html The HTML to be cleaned
23294      * return {String} The cleaned HTML
23295      */
23296     cleanHtml : function(html){
23297         html = String(html);
23298         if(html.length > 5){
23299             if(Roo.isSafari){ // strip safari nonsense
23300                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23301             }
23302         }
23303         if(html == '&nbsp;'){
23304             html = '';
23305         }
23306         return html;
23307     },
23308
23309     /**
23310      * HTML Editor -> Textarea
23311      * Protected method that will not generally be called directly. Syncs the contents
23312      * of the editor iframe with the textarea.
23313      */
23314     syncValue : function(){
23315         if(this.initialized){
23316             var bd = (this.doc.body || this.doc.documentElement);
23317             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23318             var html = bd.innerHTML;
23319             if(Roo.isSafari){
23320                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23321                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23322                 if(m && m[1]){
23323                     html = '<div style="'+m[0]+'">' + html + '</div>';
23324                 }
23325             }
23326             html = this.cleanHtml(html);
23327             // fix up the special chars.. normaly like back quotes in word...
23328             // however we do not want to do this with chinese..
23329             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23330                 
23331                 var cc = match.charCodeAt();
23332
23333                 // Get the character value, handling surrogate pairs
23334                 if (match.length == 2) {
23335                     // It's a surrogate pair, calculate the Unicode code point
23336                     var high = match.charCodeAt(0) - 0xD800;
23337                     var low  = match.charCodeAt(1) - 0xDC00;
23338                     cc = (high * 0x400) + low + 0x10000;
23339                 }  else if (
23340                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23341                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23342                     (cc >= 0xf900 && cc < 0xfb00 )
23343                 ) {
23344                         return match;
23345                 }  
23346          
23347                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23348                 return "&#" + cc + ";";
23349                 
23350                 
23351             });
23352             
23353             
23354              
23355             if(this.owner.fireEvent('beforesync', this, html) !== false){
23356                 this.el.dom.value = html;
23357                 this.owner.fireEvent('sync', this, html);
23358             }
23359         }
23360     },
23361
23362     /**
23363      * Protected method that will not generally be called directly. Pushes the value of the textarea
23364      * into the iframe editor.
23365      */
23366     pushValue : function(){
23367         if(this.initialized){
23368             var v = this.el.dom.value.trim();
23369             
23370 //            if(v.length < 1){
23371 //                v = '&#160;';
23372 //            }
23373             
23374             if(this.owner.fireEvent('beforepush', this, v) !== false){
23375                 var d = (this.doc.body || this.doc.documentElement);
23376                 d.innerHTML = v;
23377                 this.cleanUpPaste();
23378                 this.el.dom.value = d.innerHTML;
23379                 this.owner.fireEvent('push', this, v);
23380             }
23381         }
23382     },
23383
23384     // private
23385     deferFocus : function(){
23386         this.focus.defer(10, this);
23387     },
23388
23389     // doc'ed in Field
23390     focus : function(){
23391         if(this.win && !this.sourceEditMode){
23392             this.win.focus();
23393         }else{
23394             this.el.focus();
23395         }
23396     },
23397     
23398     assignDocWin: function()
23399     {
23400         var iframe = this.iframe;
23401         
23402          if(Roo.isIE){
23403             this.doc = iframe.contentWindow.document;
23404             this.win = iframe.contentWindow;
23405         } else {
23406 //            if (!Roo.get(this.frameId)) {
23407 //                return;
23408 //            }
23409 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23410 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23411             
23412             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23413                 return;
23414             }
23415             
23416             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23417             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23418         }
23419     },
23420     
23421     // private
23422     initEditor : function(){
23423         //console.log("INIT EDITOR");
23424         this.assignDocWin();
23425         
23426         
23427         
23428         this.doc.designMode="on";
23429         this.doc.open();
23430         this.doc.write(this.getDocMarkup());
23431         this.doc.close();
23432         
23433         var dbody = (this.doc.body || this.doc.documentElement);
23434         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23435         // this copies styles from the containing element into thsi one..
23436         // not sure why we need all of this..
23437         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23438         
23439         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23440         //ss['background-attachment'] = 'fixed'; // w3c
23441         dbody.bgProperties = 'fixed'; // ie
23442         //Roo.DomHelper.applyStyles(dbody, ss);
23443         Roo.EventManager.on(this.doc, {
23444             //'mousedown': this.onEditorEvent,
23445             'mouseup': this.onEditorEvent,
23446             'dblclick': this.onEditorEvent,
23447             'click': this.onEditorEvent,
23448             'keyup': this.onEditorEvent,
23449             buffer:100,
23450             scope: this
23451         });
23452         if(Roo.isGecko){
23453             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23454         }
23455         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23456             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23457         }
23458         this.initialized = true;
23459
23460         this.owner.fireEvent('initialize', this);
23461         this.pushValue();
23462     },
23463
23464     // private
23465     onDestroy : function(){
23466         
23467         
23468         
23469         if(this.rendered){
23470             
23471             //for (var i =0; i < this.toolbars.length;i++) {
23472             //    // fixme - ask toolbars for heights?
23473             //    this.toolbars[i].onDestroy();
23474            // }
23475             
23476             //this.wrap.dom.innerHTML = '';
23477             //this.wrap.remove();
23478         }
23479     },
23480
23481     // private
23482     onFirstFocus : function(){
23483         
23484         this.assignDocWin();
23485         
23486         
23487         this.activated = true;
23488          
23489     
23490         if(Roo.isGecko){ // prevent silly gecko errors
23491             this.win.focus();
23492             var s = this.win.getSelection();
23493             if(!s.focusNode || s.focusNode.nodeType != 3){
23494                 var r = s.getRangeAt(0);
23495                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23496                 r.collapse(true);
23497                 this.deferFocus();
23498             }
23499             try{
23500                 this.execCmd('useCSS', true);
23501                 this.execCmd('styleWithCSS', false);
23502             }catch(e){}
23503         }
23504         this.owner.fireEvent('activate', this);
23505     },
23506
23507     // private
23508     adjustFont: function(btn){
23509         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23510         //if(Roo.isSafari){ // safari
23511         //    adjust *= 2;
23512        // }
23513         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23514         if(Roo.isSafari){ // safari
23515             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23516             v =  (v < 10) ? 10 : v;
23517             v =  (v > 48) ? 48 : v;
23518             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23519             
23520         }
23521         
23522         
23523         v = Math.max(1, v+adjust);
23524         
23525         this.execCmd('FontSize', v  );
23526     },
23527
23528     onEditorEvent : function(e)
23529     {
23530         this.owner.fireEvent('editorevent', this, e);
23531       //  this.updateToolbar();
23532         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23533     },
23534
23535     insertTag : function(tg)
23536     {
23537         // could be a bit smarter... -> wrap the current selected tRoo..
23538         if (tg.toLowerCase() == 'span' ||
23539             tg.toLowerCase() == 'code' ||
23540             tg.toLowerCase() == 'sup' ||
23541             tg.toLowerCase() == 'sub' 
23542             ) {
23543             
23544             range = this.createRange(this.getSelection());
23545             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23546             wrappingNode.appendChild(range.extractContents());
23547             range.insertNode(wrappingNode);
23548
23549             return;
23550             
23551             
23552             
23553         }
23554         this.execCmd("formatblock",   tg);
23555         
23556     },
23557     
23558     insertText : function(txt)
23559     {
23560         
23561         
23562         var range = this.createRange();
23563         range.deleteContents();
23564                //alert(Sender.getAttribute('label'));
23565                
23566         range.insertNode(this.doc.createTextNode(txt));
23567     } ,
23568     
23569      
23570
23571     /**
23572      * Executes a Midas editor command on the editor document and performs necessary focus and
23573      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23574      * @param {String} cmd The Midas command
23575      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23576      */
23577     relayCmd : function(cmd, value){
23578         this.win.focus();
23579         this.execCmd(cmd, value);
23580         this.owner.fireEvent('editorevent', this);
23581         //this.updateToolbar();
23582         this.owner.deferFocus();
23583     },
23584
23585     /**
23586      * Executes a Midas editor command directly on the editor document.
23587      * For visual commands, you should use {@link #relayCmd} instead.
23588      * <b>This should only be called after the editor is initialized.</b>
23589      * @param {String} cmd The Midas command
23590      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23591      */
23592     execCmd : function(cmd, value){
23593         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23594         this.syncValue();
23595     },
23596  
23597  
23598    
23599     /**
23600      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23601      * to insert tRoo.
23602      * @param {String} text | dom node.. 
23603      */
23604     insertAtCursor : function(text)
23605     {
23606         
23607         if(!this.activated){
23608             return;
23609         }
23610         /*
23611         if(Roo.isIE){
23612             this.win.focus();
23613             var r = this.doc.selection.createRange();
23614             if(r){
23615                 r.collapse(true);
23616                 r.pasteHTML(text);
23617                 this.syncValue();
23618                 this.deferFocus();
23619             
23620             }
23621             return;
23622         }
23623         */
23624         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23625             this.win.focus();
23626             
23627             
23628             // from jquery ui (MIT licenced)
23629             var range, node;
23630             var win = this.win;
23631             
23632             if (win.getSelection && win.getSelection().getRangeAt) {
23633                 range = win.getSelection().getRangeAt(0);
23634                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23635                 range.insertNode(node);
23636             } else if (win.document.selection && win.document.selection.createRange) {
23637                 // no firefox support
23638                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23639                 win.document.selection.createRange().pasteHTML(txt);
23640             } else {
23641                 // no firefox support
23642                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23643                 this.execCmd('InsertHTML', txt);
23644             } 
23645             
23646             this.syncValue();
23647             
23648             this.deferFocus();
23649         }
23650     },
23651  // private
23652     mozKeyPress : function(e){
23653         if(e.ctrlKey){
23654             var c = e.getCharCode(), cmd;
23655           
23656             if(c > 0){
23657                 c = String.fromCharCode(c).toLowerCase();
23658                 switch(c){
23659                     case 'b':
23660                         cmd = 'bold';
23661                         break;
23662                     case 'i':
23663                         cmd = 'italic';
23664                         break;
23665                     
23666                     case 'u':
23667                         cmd = 'underline';
23668                         break;
23669                     
23670                     case 'v':
23671                         this.cleanUpPaste.defer(100, this);
23672                         return;
23673                         
23674                 }
23675                 if(cmd){
23676                     this.win.focus();
23677                     this.execCmd(cmd);
23678                     this.deferFocus();
23679                     e.preventDefault();
23680                 }
23681                 
23682             }
23683         }
23684     },
23685
23686     // private
23687     fixKeys : function(){ // load time branching for fastest keydown performance
23688         if(Roo.isIE){
23689             return function(e){
23690                 var k = e.getKey(), r;
23691                 if(k == e.TAB){
23692                     e.stopEvent();
23693                     r = this.doc.selection.createRange();
23694                     if(r){
23695                         r.collapse(true);
23696                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23697                         this.deferFocus();
23698                     }
23699                     return;
23700                 }
23701                 
23702                 if(k == e.ENTER){
23703                     r = this.doc.selection.createRange();
23704                     if(r){
23705                         var target = r.parentElement();
23706                         if(!target || target.tagName.toLowerCase() != 'li'){
23707                             e.stopEvent();
23708                             r.pasteHTML('<br />');
23709                             r.collapse(false);
23710                             r.select();
23711                         }
23712                     }
23713                 }
23714                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23715                     this.cleanUpPaste.defer(100, this);
23716                     return;
23717                 }
23718                 
23719                 
23720             };
23721         }else if(Roo.isOpera){
23722             return function(e){
23723                 var k = e.getKey();
23724                 if(k == e.TAB){
23725                     e.stopEvent();
23726                     this.win.focus();
23727                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23728                     this.deferFocus();
23729                 }
23730                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23731                     this.cleanUpPaste.defer(100, this);
23732                     return;
23733                 }
23734                 
23735             };
23736         }else if(Roo.isSafari){
23737             return function(e){
23738                 var k = e.getKey();
23739                 
23740                 if(k == e.TAB){
23741                     e.stopEvent();
23742                     this.execCmd('InsertText','\t');
23743                     this.deferFocus();
23744                     return;
23745                 }
23746                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23747                     this.cleanUpPaste.defer(100, this);
23748                     return;
23749                 }
23750                 
23751              };
23752         }
23753     }(),
23754     
23755     getAllAncestors: function()
23756     {
23757         var p = this.getSelectedNode();
23758         var a = [];
23759         if (!p) {
23760             a.push(p); // push blank onto stack..
23761             p = this.getParentElement();
23762         }
23763         
23764         
23765         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23766             a.push(p);
23767             p = p.parentNode;
23768         }
23769         a.push(this.doc.body);
23770         return a;
23771     },
23772     lastSel : false,
23773     lastSelNode : false,
23774     
23775     
23776     getSelection : function() 
23777     {
23778         this.assignDocWin();
23779         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23780     },
23781     
23782     getSelectedNode: function() 
23783     {
23784         // this may only work on Gecko!!!
23785         
23786         // should we cache this!!!!
23787         
23788         
23789         
23790          
23791         var range = this.createRange(this.getSelection()).cloneRange();
23792         
23793         if (Roo.isIE) {
23794             var parent = range.parentElement();
23795             while (true) {
23796                 var testRange = range.duplicate();
23797                 testRange.moveToElementText(parent);
23798                 if (testRange.inRange(range)) {
23799                     break;
23800                 }
23801                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23802                     break;
23803                 }
23804                 parent = parent.parentElement;
23805             }
23806             return parent;
23807         }
23808         
23809         // is ancestor a text element.
23810         var ac =  range.commonAncestorContainer;
23811         if (ac.nodeType == 3) {
23812             ac = ac.parentNode;
23813         }
23814         
23815         var ar = ac.childNodes;
23816          
23817         var nodes = [];
23818         var other_nodes = [];
23819         var has_other_nodes = false;
23820         for (var i=0;i<ar.length;i++) {
23821             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23822                 continue;
23823             }
23824             // fullly contained node.
23825             
23826             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23827                 nodes.push(ar[i]);
23828                 continue;
23829             }
23830             
23831             // probably selected..
23832             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23833                 other_nodes.push(ar[i]);
23834                 continue;
23835             }
23836             // outer..
23837             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23838                 continue;
23839             }
23840             
23841             
23842             has_other_nodes = true;
23843         }
23844         if (!nodes.length && other_nodes.length) {
23845             nodes= other_nodes;
23846         }
23847         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23848             return false;
23849         }
23850         
23851         return nodes[0];
23852     },
23853     createRange: function(sel)
23854     {
23855         // this has strange effects when using with 
23856         // top toolbar - not sure if it's a great idea.
23857         //this.editor.contentWindow.focus();
23858         if (typeof sel != "undefined") {
23859             try {
23860                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23861             } catch(e) {
23862                 return this.doc.createRange();
23863             }
23864         } else {
23865             return this.doc.createRange();
23866         }
23867     },
23868     getParentElement: function()
23869     {
23870         
23871         this.assignDocWin();
23872         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23873         
23874         var range = this.createRange(sel);
23875          
23876         try {
23877             var p = range.commonAncestorContainer;
23878             while (p.nodeType == 3) { // text node
23879                 p = p.parentNode;
23880             }
23881             return p;
23882         } catch (e) {
23883             return null;
23884         }
23885     
23886     },
23887     /***
23888      *
23889      * Range intersection.. the hard stuff...
23890      *  '-1' = before
23891      *  '0' = hits..
23892      *  '1' = after.
23893      *         [ -- selected range --- ]
23894      *   [fail]                        [fail]
23895      *
23896      *    basically..
23897      *      if end is before start or  hits it. fail.
23898      *      if start is after end or hits it fail.
23899      *
23900      *   if either hits (but other is outside. - then it's not 
23901      *   
23902      *    
23903      **/
23904     
23905     
23906     // @see http://www.thismuchiknow.co.uk/?p=64.
23907     rangeIntersectsNode : function(range, node)
23908     {
23909         var nodeRange = node.ownerDocument.createRange();
23910         try {
23911             nodeRange.selectNode(node);
23912         } catch (e) {
23913             nodeRange.selectNodeContents(node);
23914         }
23915     
23916         var rangeStartRange = range.cloneRange();
23917         rangeStartRange.collapse(true);
23918     
23919         var rangeEndRange = range.cloneRange();
23920         rangeEndRange.collapse(false);
23921     
23922         var nodeStartRange = nodeRange.cloneRange();
23923         nodeStartRange.collapse(true);
23924     
23925         var nodeEndRange = nodeRange.cloneRange();
23926         nodeEndRange.collapse(false);
23927     
23928         return rangeStartRange.compareBoundaryPoints(
23929                  Range.START_TO_START, nodeEndRange) == -1 &&
23930                rangeEndRange.compareBoundaryPoints(
23931                  Range.START_TO_START, nodeStartRange) == 1;
23932         
23933          
23934     },
23935     rangeCompareNode : function(range, node)
23936     {
23937         var nodeRange = node.ownerDocument.createRange();
23938         try {
23939             nodeRange.selectNode(node);
23940         } catch (e) {
23941             nodeRange.selectNodeContents(node);
23942         }
23943         
23944         
23945         range.collapse(true);
23946     
23947         nodeRange.collapse(true);
23948      
23949         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23950         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23951          
23952         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23953         
23954         var nodeIsBefore   =  ss == 1;
23955         var nodeIsAfter    = ee == -1;
23956         
23957         if (nodeIsBefore && nodeIsAfter) {
23958             return 0; // outer
23959         }
23960         if (!nodeIsBefore && nodeIsAfter) {
23961             return 1; //right trailed.
23962         }
23963         
23964         if (nodeIsBefore && !nodeIsAfter) {
23965             return 2;  // left trailed.
23966         }
23967         // fully contined.
23968         return 3;
23969     },
23970
23971     // private? - in a new class?
23972     cleanUpPaste :  function()
23973     {
23974         // cleans up the whole document..
23975         Roo.log('cleanuppaste');
23976         
23977         this.cleanUpChildren(this.doc.body);
23978         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23979         if (clean != this.doc.body.innerHTML) {
23980             this.doc.body.innerHTML = clean;
23981         }
23982         
23983     },
23984     
23985     cleanWordChars : function(input) {// change the chars to hex code
23986         var he = Roo.HtmlEditorCore;
23987         
23988         var output = input;
23989         Roo.each(he.swapCodes, function(sw) { 
23990             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23991             
23992             output = output.replace(swapper, sw[1]);
23993         });
23994         
23995         return output;
23996     },
23997     
23998     
23999     cleanUpChildren : function (n)
24000     {
24001         if (!n.childNodes.length) {
24002             return;
24003         }
24004         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24005            this.cleanUpChild(n.childNodes[i]);
24006         }
24007     },
24008     
24009     
24010         
24011     
24012     cleanUpChild : function (node)
24013     {
24014         var ed = this;
24015         //console.log(node);
24016         if (node.nodeName == "#text") {
24017             // clean up silly Windows -- stuff?
24018             return; 
24019         }
24020         if (node.nodeName == "#comment") {
24021             node.parentNode.removeChild(node);
24022             // clean up silly Windows -- stuff?
24023             return; 
24024         }
24025         var lcname = node.tagName.toLowerCase();
24026         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24027         // whitelist of tags..
24028         
24029         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24030             // remove node.
24031             node.parentNode.removeChild(node);
24032             return;
24033             
24034         }
24035         
24036         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24037         
24038         // spans with no attributes - just remove them..
24039         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24040             remove_keep_children = true;
24041         }
24042         
24043         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24044         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24045         
24046         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24047         //    remove_keep_children = true;
24048         //}
24049         
24050         if (remove_keep_children) {
24051             this.cleanUpChildren(node);
24052             // inserts everything just before this node...
24053             while (node.childNodes.length) {
24054                 var cn = node.childNodes[0];
24055                 node.removeChild(cn);
24056                 node.parentNode.insertBefore(cn, node);
24057             }
24058             node.parentNode.removeChild(node);
24059             return;
24060         }
24061         
24062         if (!node.attributes || !node.attributes.length) {
24063             
24064           
24065             
24066             
24067             this.cleanUpChildren(node);
24068             return;
24069         }
24070         
24071         function cleanAttr(n,v)
24072         {
24073             
24074             if (v.match(/^\./) || v.match(/^\//)) {
24075                 return;
24076             }
24077             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24078                 return;
24079             }
24080             if (v.match(/^#/)) {
24081                 return;
24082             }
24083 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24084             node.removeAttribute(n);
24085             
24086         }
24087         
24088         var cwhite = this.cwhite;
24089         var cblack = this.cblack;
24090             
24091         function cleanStyle(n,v)
24092         {
24093             if (v.match(/expression/)) { //XSS?? should we even bother..
24094                 node.removeAttribute(n);
24095                 return;
24096             }
24097             
24098             var parts = v.split(/;/);
24099             var clean = [];
24100             
24101             Roo.each(parts, function(p) {
24102                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24103                 if (!p.length) {
24104                     return true;
24105                 }
24106                 var l = p.split(':').shift().replace(/\s+/g,'');
24107                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24108                 
24109                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24110 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24111                     //node.removeAttribute(n);
24112                     return true;
24113                 }
24114                 //Roo.log()
24115                 // only allow 'c whitelisted system attributes'
24116                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24117 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24118                     //node.removeAttribute(n);
24119                     return true;
24120                 }
24121                 
24122                 
24123                  
24124                 
24125                 clean.push(p);
24126                 return true;
24127             });
24128             if (clean.length) { 
24129                 node.setAttribute(n, clean.join(';'));
24130             } else {
24131                 node.removeAttribute(n);
24132             }
24133             
24134         }
24135         
24136         
24137         for (var i = node.attributes.length-1; i > -1 ; i--) {
24138             var a = node.attributes[i];
24139             //console.log(a);
24140             
24141             if (a.name.toLowerCase().substr(0,2)=='on')  {
24142                 node.removeAttribute(a.name);
24143                 continue;
24144             }
24145             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24146                 node.removeAttribute(a.name);
24147                 continue;
24148             }
24149             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24150                 cleanAttr(a.name,a.value); // fixme..
24151                 continue;
24152             }
24153             if (a.name == 'style') {
24154                 cleanStyle(a.name,a.value);
24155                 continue;
24156             }
24157             /// clean up MS crap..
24158             // tecnically this should be a list of valid class'es..
24159             
24160             
24161             if (a.name == 'class') {
24162                 if (a.value.match(/^Mso/)) {
24163                     node.removeAttribute('class');
24164                 }
24165                 
24166                 if (a.value.match(/^body$/)) {
24167                     node.removeAttribute('class');
24168                 }
24169                 continue;
24170             }
24171             
24172             // style cleanup!?
24173             // class cleanup?
24174             
24175         }
24176         
24177         
24178         this.cleanUpChildren(node);
24179         
24180         
24181     },
24182     
24183     /**
24184      * Clean up MS wordisms...
24185      */
24186     cleanWord : function(node)
24187     {
24188         if (!node) {
24189             this.cleanWord(this.doc.body);
24190             return;
24191         }
24192         
24193         if(
24194                 node.nodeName == 'SPAN' &&
24195                 !node.hasAttributes() &&
24196                 node.childNodes.length == 1 &&
24197                 node.firstChild.nodeName == "#text"  
24198         ) {
24199             var textNode = node.firstChild;
24200             node.removeChild(textNode);
24201             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24202                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24203             }
24204             node.parentNode.insertBefore(textNode, node);
24205             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24206                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24207             }
24208             node.parentNode.removeChild(node);
24209         }
24210         
24211         if (node.nodeName == "#text") {
24212             // clean up silly Windows -- stuff?
24213             return; 
24214         }
24215         if (node.nodeName == "#comment") {
24216             node.parentNode.removeChild(node);
24217             // clean up silly Windows -- stuff?
24218             return; 
24219         }
24220         
24221         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24222             node.parentNode.removeChild(node);
24223             return;
24224         }
24225         //Roo.log(node.tagName);
24226         // remove - but keep children..
24227         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24228             //Roo.log('-- removed');
24229             while (node.childNodes.length) {
24230                 var cn = node.childNodes[0];
24231                 node.removeChild(cn);
24232                 node.parentNode.insertBefore(cn, node);
24233                 // move node to parent - and clean it..
24234                 this.cleanWord(cn);
24235             }
24236             node.parentNode.removeChild(node);
24237             /// no need to iterate chidlren = it's got none..
24238             //this.iterateChildren(node, this.cleanWord);
24239             return;
24240         }
24241         // clean styles
24242         if (node.className.length) {
24243             
24244             var cn = node.className.split(/\W+/);
24245             var cna = [];
24246             Roo.each(cn, function(cls) {
24247                 if (cls.match(/Mso[a-zA-Z]+/)) {
24248                     return;
24249                 }
24250                 cna.push(cls);
24251             });
24252             node.className = cna.length ? cna.join(' ') : '';
24253             if (!cna.length) {
24254                 node.removeAttribute("class");
24255             }
24256         }
24257         
24258         if (node.hasAttribute("lang")) {
24259             node.removeAttribute("lang");
24260         }
24261         
24262         if (node.hasAttribute("style")) {
24263             
24264             var styles = node.getAttribute("style").split(";");
24265             var nstyle = [];
24266             Roo.each(styles, function(s) {
24267                 if (!s.match(/:/)) {
24268                     return;
24269                 }
24270                 var kv = s.split(":");
24271                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24272                     return;
24273                 }
24274                 // what ever is left... we allow.
24275                 nstyle.push(s);
24276             });
24277             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24278             if (!nstyle.length) {
24279                 node.removeAttribute('style');
24280             }
24281         }
24282         this.iterateChildren(node, this.cleanWord);
24283         
24284         
24285         
24286     },
24287     /**
24288      * iterateChildren of a Node, calling fn each time, using this as the scole..
24289      * @param {DomNode} node node to iterate children of.
24290      * @param {Function} fn method of this class to call on each item.
24291      */
24292     iterateChildren : function(node, fn)
24293     {
24294         if (!node.childNodes.length) {
24295                 return;
24296         }
24297         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24298            fn.call(this, node.childNodes[i])
24299         }
24300     },
24301     
24302     
24303     /**
24304      * cleanTableWidths.
24305      *
24306      * Quite often pasting from word etc.. results in tables with column and widths.
24307      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24308      *
24309      */
24310     cleanTableWidths : function(node)
24311     {
24312          
24313          
24314         if (!node) {
24315             this.cleanTableWidths(this.doc.body);
24316             return;
24317         }
24318         
24319         // ignore list...
24320         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24321             return; 
24322         }
24323         Roo.log(node.tagName);
24324         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24325             this.iterateChildren(node, this.cleanTableWidths);
24326             return;
24327         }
24328         if (node.hasAttribute('width')) {
24329             node.removeAttribute('width');
24330         }
24331         
24332          
24333         if (node.hasAttribute("style")) {
24334             // pretty basic...
24335             
24336             var styles = node.getAttribute("style").split(";");
24337             var nstyle = [];
24338             Roo.each(styles, function(s) {
24339                 if (!s.match(/:/)) {
24340                     return;
24341                 }
24342                 var kv = s.split(":");
24343                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24344                     return;
24345                 }
24346                 // what ever is left... we allow.
24347                 nstyle.push(s);
24348             });
24349             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24350             if (!nstyle.length) {
24351                 node.removeAttribute('style');
24352             }
24353         }
24354         
24355         this.iterateChildren(node, this.cleanTableWidths);
24356         
24357         
24358     },
24359     
24360     
24361     
24362     
24363     domToHTML : function(currentElement, depth, nopadtext) {
24364         
24365         depth = depth || 0;
24366         nopadtext = nopadtext || false;
24367     
24368         if (!currentElement) {
24369             return this.domToHTML(this.doc.body);
24370         }
24371         
24372         //Roo.log(currentElement);
24373         var j;
24374         var allText = false;
24375         var nodeName = currentElement.nodeName;
24376         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24377         
24378         if  (nodeName == '#text') {
24379             
24380             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24381         }
24382         
24383         
24384         var ret = '';
24385         if (nodeName != 'BODY') {
24386              
24387             var i = 0;
24388             // Prints the node tagName, such as <A>, <IMG>, etc
24389             if (tagName) {
24390                 var attr = [];
24391                 for(i = 0; i < currentElement.attributes.length;i++) {
24392                     // quoting?
24393                     var aname = currentElement.attributes.item(i).name;
24394                     if (!currentElement.attributes.item(i).value.length) {
24395                         continue;
24396                     }
24397                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24398                 }
24399                 
24400                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24401             } 
24402             else {
24403                 
24404                 // eack
24405             }
24406         } else {
24407             tagName = false;
24408         }
24409         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24410             return ret;
24411         }
24412         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24413             nopadtext = true;
24414         }
24415         
24416         
24417         // Traverse the tree
24418         i = 0;
24419         var currentElementChild = currentElement.childNodes.item(i);
24420         var allText = true;
24421         var innerHTML  = '';
24422         lastnode = '';
24423         while (currentElementChild) {
24424             // Formatting code (indent the tree so it looks nice on the screen)
24425             var nopad = nopadtext;
24426             if (lastnode == 'SPAN') {
24427                 nopad  = true;
24428             }
24429             // text
24430             if  (currentElementChild.nodeName == '#text') {
24431                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24432                 toadd = nopadtext ? toadd : toadd.trim();
24433                 if (!nopad && toadd.length > 80) {
24434                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24435                 }
24436                 innerHTML  += toadd;
24437                 
24438                 i++;
24439                 currentElementChild = currentElement.childNodes.item(i);
24440                 lastNode = '';
24441                 continue;
24442             }
24443             allText = false;
24444             
24445             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24446                 
24447             // Recursively traverse the tree structure of the child node
24448             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24449             lastnode = currentElementChild.nodeName;
24450             i++;
24451             currentElementChild=currentElement.childNodes.item(i);
24452         }
24453         
24454         ret += innerHTML;
24455         
24456         if (!allText) {
24457                 // The remaining code is mostly for formatting the tree
24458             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24459         }
24460         
24461         
24462         if (tagName) {
24463             ret+= "</"+tagName+">";
24464         }
24465         return ret;
24466         
24467     },
24468         
24469     applyBlacklists : function()
24470     {
24471         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24472         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24473         
24474         this.white = [];
24475         this.black = [];
24476         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24477             if (b.indexOf(tag) > -1) {
24478                 return;
24479             }
24480             this.white.push(tag);
24481             
24482         }, this);
24483         
24484         Roo.each(w, function(tag) {
24485             if (b.indexOf(tag) > -1) {
24486                 return;
24487             }
24488             if (this.white.indexOf(tag) > -1) {
24489                 return;
24490             }
24491             this.white.push(tag);
24492             
24493         }, this);
24494         
24495         
24496         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24497             if (w.indexOf(tag) > -1) {
24498                 return;
24499             }
24500             this.black.push(tag);
24501             
24502         }, this);
24503         
24504         Roo.each(b, function(tag) {
24505             if (w.indexOf(tag) > -1) {
24506                 return;
24507             }
24508             if (this.black.indexOf(tag) > -1) {
24509                 return;
24510             }
24511             this.black.push(tag);
24512             
24513         }, this);
24514         
24515         
24516         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24517         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24518         
24519         this.cwhite = [];
24520         this.cblack = [];
24521         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24522             if (b.indexOf(tag) > -1) {
24523                 return;
24524             }
24525             this.cwhite.push(tag);
24526             
24527         }, this);
24528         
24529         Roo.each(w, function(tag) {
24530             if (b.indexOf(tag) > -1) {
24531                 return;
24532             }
24533             if (this.cwhite.indexOf(tag) > -1) {
24534                 return;
24535             }
24536             this.cwhite.push(tag);
24537             
24538         }, this);
24539         
24540         
24541         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24542             if (w.indexOf(tag) > -1) {
24543                 return;
24544             }
24545             this.cblack.push(tag);
24546             
24547         }, this);
24548         
24549         Roo.each(b, function(tag) {
24550             if (w.indexOf(tag) > -1) {
24551                 return;
24552             }
24553             if (this.cblack.indexOf(tag) > -1) {
24554                 return;
24555             }
24556             this.cblack.push(tag);
24557             
24558         }, this);
24559     },
24560     
24561     setStylesheets : function(stylesheets)
24562     {
24563         if(typeof(stylesheets) == 'string'){
24564             Roo.get(this.iframe.contentDocument.head).createChild({
24565                 tag : 'link',
24566                 rel : 'stylesheet',
24567                 type : 'text/css',
24568                 href : stylesheets
24569             });
24570             
24571             return;
24572         }
24573         var _this = this;
24574      
24575         Roo.each(stylesheets, function(s) {
24576             if(!s.length){
24577                 return;
24578             }
24579             
24580             Roo.get(_this.iframe.contentDocument.head).createChild({
24581                 tag : 'link',
24582                 rel : 'stylesheet',
24583                 type : 'text/css',
24584                 href : s
24585             });
24586         });
24587
24588         
24589     },
24590     
24591     removeStylesheets : function()
24592     {
24593         var _this = this;
24594         
24595         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24596             s.remove();
24597         });
24598     },
24599     
24600     setStyle : function(style)
24601     {
24602         Roo.get(this.iframe.contentDocument.head).createChild({
24603             tag : 'style',
24604             type : 'text/css',
24605             html : style
24606         });
24607
24608         return;
24609     }
24610     
24611     // hide stuff that is not compatible
24612     /**
24613      * @event blur
24614      * @hide
24615      */
24616     /**
24617      * @event change
24618      * @hide
24619      */
24620     /**
24621      * @event focus
24622      * @hide
24623      */
24624     /**
24625      * @event specialkey
24626      * @hide
24627      */
24628     /**
24629      * @cfg {String} fieldClass @hide
24630      */
24631     /**
24632      * @cfg {String} focusClass @hide
24633      */
24634     /**
24635      * @cfg {String} autoCreate @hide
24636      */
24637     /**
24638      * @cfg {String} inputType @hide
24639      */
24640     /**
24641      * @cfg {String} invalidClass @hide
24642      */
24643     /**
24644      * @cfg {String} invalidText @hide
24645      */
24646     /**
24647      * @cfg {String} msgFx @hide
24648      */
24649     /**
24650      * @cfg {String} validateOnBlur @hide
24651      */
24652 });
24653
24654 Roo.HtmlEditorCore.white = [
24655         'area', 'br', 'img', 'input', 'hr', 'wbr',
24656         
24657        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24658        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24659        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24660        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24661        'table',   'ul',         'xmp', 
24662        
24663        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24664       'thead',   'tr', 
24665      
24666       'dir', 'menu', 'ol', 'ul', 'dl',
24667        
24668       'embed',  'object'
24669 ];
24670
24671
24672 Roo.HtmlEditorCore.black = [
24673     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24674         'applet', // 
24675         'base',   'basefont', 'bgsound', 'blink',  'body', 
24676         'frame',  'frameset', 'head',    'html',   'ilayer', 
24677         'iframe', 'layer',  'link',     'meta',    'object',   
24678         'script', 'style' ,'title',  'xml' // clean later..
24679 ];
24680 Roo.HtmlEditorCore.clean = [
24681     'script', 'style', 'title', 'xml'
24682 ];
24683 Roo.HtmlEditorCore.remove = [
24684     'font'
24685 ];
24686 // attributes..
24687
24688 Roo.HtmlEditorCore.ablack = [
24689     'on'
24690 ];
24691     
24692 Roo.HtmlEditorCore.aclean = [ 
24693     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24694 ];
24695
24696 // protocols..
24697 Roo.HtmlEditorCore.pwhite= [
24698         'http',  'https',  'mailto'
24699 ];
24700
24701 // white listed style attributes.
24702 Roo.HtmlEditorCore.cwhite= [
24703       //  'text-align', /// default is to allow most things..
24704       
24705          
24706 //        'font-size'//??
24707 ];
24708
24709 // black listed style attributes.
24710 Roo.HtmlEditorCore.cblack= [
24711       //  'font-size' -- this can be set by the project 
24712 ];
24713
24714
24715 Roo.HtmlEditorCore.swapCodes   =[ 
24716     [    8211, "--" ], 
24717     [    8212, "--" ], 
24718     [    8216,  "'" ],  
24719     [    8217, "'" ],  
24720     [    8220, '"' ],  
24721     [    8221, '"' ],  
24722     [    8226, "*" ],  
24723     [    8230, "..." ]
24724 ]; 
24725
24726     /*
24727  * - LGPL
24728  *
24729  * HtmlEditor
24730  * 
24731  */
24732
24733 /**
24734  * @class Roo.bootstrap.HtmlEditor
24735  * @extends Roo.bootstrap.TextArea
24736  * Bootstrap HtmlEditor class
24737
24738  * @constructor
24739  * Create a new HtmlEditor
24740  * @param {Object} config The config object
24741  */
24742
24743 Roo.bootstrap.HtmlEditor = function(config){
24744     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24745     if (!this.toolbars) {
24746         this.toolbars = [];
24747     }
24748     
24749     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24750     this.addEvents({
24751             /**
24752              * @event initialize
24753              * Fires when the editor is fully initialized (including the iframe)
24754              * @param {HtmlEditor} this
24755              */
24756             initialize: true,
24757             /**
24758              * @event activate
24759              * Fires when the editor is first receives the focus. Any insertion must wait
24760              * until after this event.
24761              * @param {HtmlEditor} this
24762              */
24763             activate: true,
24764              /**
24765              * @event beforesync
24766              * Fires before the textarea is updated with content from the editor iframe. Return false
24767              * to cancel the sync.
24768              * @param {HtmlEditor} this
24769              * @param {String} html
24770              */
24771             beforesync: true,
24772              /**
24773              * @event beforepush
24774              * Fires before the iframe editor is updated with content from the textarea. Return false
24775              * to cancel the push.
24776              * @param {HtmlEditor} this
24777              * @param {String} html
24778              */
24779             beforepush: true,
24780              /**
24781              * @event sync
24782              * Fires when the textarea is updated with content from the editor iframe.
24783              * @param {HtmlEditor} this
24784              * @param {String} html
24785              */
24786             sync: true,
24787              /**
24788              * @event push
24789              * Fires when the iframe editor is updated with content from the textarea.
24790              * @param {HtmlEditor} this
24791              * @param {String} html
24792              */
24793             push: true,
24794              /**
24795              * @event editmodechange
24796              * Fires when the editor switches edit modes
24797              * @param {HtmlEditor} this
24798              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24799              */
24800             editmodechange: true,
24801             /**
24802              * @event editorevent
24803              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24804              * @param {HtmlEditor} this
24805              */
24806             editorevent: true,
24807             /**
24808              * @event firstfocus
24809              * Fires when on first focus - needed by toolbars..
24810              * @param {HtmlEditor} this
24811              */
24812             firstfocus: true,
24813             /**
24814              * @event autosave
24815              * Auto save the htmlEditor value as a file into Events
24816              * @param {HtmlEditor} this
24817              */
24818             autosave: true,
24819             /**
24820              * @event savedpreview
24821              * preview the saved version of htmlEditor
24822              * @param {HtmlEditor} this
24823              */
24824             savedpreview: true
24825         });
24826 };
24827
24828
24829 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24830     
24831     
24832       /**
24833      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24834      */
24835     toolbars : false,
24836     
24837      /**
24838     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24839     */
24840     btns : [],
24841    
24842      /**
24843      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24844      *                        Roo.resizable.
24845      */
24846     resizable : false,
24847      /**
24848      * @cfg {Number} height (in pixels)
24849      */   
24850     height: 300,
24851    /**
24852      * @cfg {Number} width (in pixels)
24853      */   
24854     width: false,
24855     
24856     /**
24857      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24858      * 
24859      */
24860     stylesheets: false,
24861     
24862     // id of frame..
24863     frameId: false,
24864     
24865     // private properties
24866     validationEvent : false,
24867     deferHeight: true,
24868     initialized : false,
24869     activated : false,
24870     
24871     onFocus : Roo.emptyFn,
24872     iframePad:3,
24873     hideMode:'offsets',
24874     
24875     tbContainer : false,
24876     
24877     bodyCls : '',
24878     
24879     toolbarContainer :function() {
24880         return this.wrap.select('.x-html-editor-tb',true).first();
24881     },
24882
24883     /**
24884      * Protected method that will not generally be called directly. It
24885      * is called when the editor creates its toolbar. Override this method if you need to
24886      * add custom toolbar buttons.
24887      * @param {HtmlEditor} editor
24888      */
24889     createToolbar : function(){
24890         Roo.log('renewing');
24891         Roo.log("create toolbars");
24892         
24893         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24894         this.toolbars[0].render(this.toolbarContainer());
24895         
24896         return;
24897         
24898 //        if (!editor.toolbars || !editor.toolbars.length) {
24899 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24900 //        }
24901 //        
24902 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24903 //            editor.toolbars[i] = Roo.factory(
24904 //                    typeof(editor.toolbars[i]) == 'string' ?
24905 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24906 //                Roo.bootstrap.HtmlEditor);
24907 //            editor.toolbars[i].init(editor);
24908 //        }
24909     },
24910
24911      
24912     // private
24913     onRender : function(ct, position)
24914     {
24915        // Roo.log("Call onRender: " + this.xtype);
24916         var _t = this;
24917         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24918       
24919         this.wrap = this.inputEl().wrap({
24920             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24921         });
24922         
24923         this.editorcore.onRender(ct, position);
24924          
24925         if (this.resizable) {
24926             this.resizeEl = new Roo.Resizable(this.wrap, {
24927                 pinned : true,
24928                 wrap: true,
24929                 dynamic : true,
24930                 minHeight : this.height,
24931                 height: this.height,
24932                 handles : this.resizable,
24933                 width: this.width,
24934                 listeners : {
24935                     resize : function(r, w, h) {
24936                         _t.onResize(w,h); // -something
24937                     }
24938                 }
24939             });
24940             
24941         }
24942         this.createToolbar(this);
24943        
24944         
24945         if(!this.width && this.resizable){
24946             this.setSize(this.wrap.getSize());
24947         }
24948         if (this.resizeEl) {
24949             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24950             // should trigger onReize..
24951         }
24952         
24953     },
24954
24955     // private
24956     onResize : function(w, h)
24957     {
24958         Roo.log('resize: ' +w + ',' + h );
24959         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24960         var ew = false;
24961         var eh = false;
24962         
24963         if(this.inputEl() ){
24964             if(typeof w == 'number'){
24965                 var aw = w - this.wrap.getFrameWidth('lr');
24966                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24967                 ew = aw;
24968             }
24969             if(typeof h == 'number'){
24970                  var tbh = -11;  // fixme it needs to tool bar size!
24971                 for (var i =0; i < this.toolbars.length;i++) {
24972                     // fixme - ask toolbars for heights?
24973                     tbh += this.toolbars[i].el.getHeight();
24974                     //if (this.toolbars[i].footer) {
24975                     //    tbh += this.toolbars[i].footer.el.getHeight();
24976                     //}
24977                 }
24978               
24979                 
24980                 
24981                 
24982                 
24983                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24984                 ah -= 5; // knock a few pixes off for look..
24985                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24986                 var eh = ah;
24987             }
24988         }
24989         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24990         this.editorcore.onResize(ew,eh);
24991         
24992     },
24993
24994     /**
24995      * Toggles the editor between standard and source edit mode.
24996      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24997      */
24998     toggleSourceEdit : function(sourceEditMode)
24999     {
25000         this.editorcore.toggleSourceEdit(sourceEditMode);
25001         
25002         if(this.editorcore.sourceEditMode){
25003             Roo.log('editor - showing textarea');
25004             
25005 //            Roo.log('in');
25006 //            Roo.log(this.syncValue());
25007             this.syncValue();
25008             this.inputEl().removeClass(['hide', 'x-hidden']);
25009             this.inputEl().dom.removeAttribute('tabIndex');
25010             this.inputEl().focus();
25011         }else{
25012             Roo.log('editor - hiding textarea');
25013 //            Roo.log('out')
25014 //            Roo.log(this.pushValue()); 
25015             this.pushValue();
25016             
25017             this.inputEl().addClass(['hide', 'x-hidden']);
25018             this.inputEl().dom.setAttribute('tabIndex', -1);
25019             //this.deferFocus();
25020         }
25021          
25022         if(this.resizable){
25023             this.setSize(this.wrap.getSize());
25024         }
25025         
25026         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25027     },
25028  
25029     // private (for BoxComponent)
25030     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25031
25032     // private (for BoxComponent)
25033     getResizeEl : function(){
25034         return this.wrap;
25035     },
25036
25037     // private (for BoxComponent)
25038     getPositionEl : function(){
25039         return this.wrap;
25040     },
25041
25042     // private
25043     initEvents : function(){
25044         this.originalValue = this.getValue();
25045     },
25046
25047 //    /**
25048 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25049 //     * @method
25050 //     */
25051 //    markInvalid : Roo.emptyFn,
25052 //    /**
25053 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25054 //     * @method
25055 //     */
25056 //    clearInvalid : Roo.emptyFn,
25057
25058     setValue : function(v){
25059         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25060         this.editorcore.pushValue();
25061     },
25062
25063      
25064     // private
25065     deferFocus : function(){
25066         this.focus.defer(10, this);
25067     },
25068
25069     // doc'ed in Field
25070     focus : function(){
25071         this.editorcore.focus();
25072         
25073     },
25074       
25075
25076     // private
25077     onDestroy : function(){
25078         
25079         
25080         
25081         if(this.rendered){
25082             
25083             for (var i =0; i < this.toolbars.length;i++) {
25084                 // fixme - ask toolbars for heights?
25085                 this.toolbars[i].onDestroy();
25086             }
25087             
25088             this.wrap.dom.innerHTML = '';
25089             this.wrap.remove();
25090         }
25091     },
25092
25093     // private
25094     onFirstFocus : function(){
25095         //Roo.log("onFirstFocus");
25096         this.editorcore.onFirstFocus();
25097          for (var i =0; i < this.toolbars.length;i++) {
25098             this.toolbars[i].onFirstFocus();
25099         }
25100         
25101     },
25102     
25103     // private
25104     syncValue : function()
25105     {   
25106         this.editorcore.syncValue();
25107     },
25108     
25109     pushValue : function()
25110     {   
25111         this.editorcore.pushValue();
25112     }
25113      
25114     
25115     // hide stuff that is not compatible
25116     /**
25117      * @event blur
25118      * @hide
25119      */
25120     /**
25121      * @event change
25122      * @hide
25123      */
25124     /**
25125      * @event focus
25126      * @hide
25127      */
25128     /**
25129      * @event specialkey
25130      * @hide
25131      */
25132     /**
25133      * @cfg {String} fieldClass @hide
25134      */
25135     /**
25136      * @cfg {String} focusClass @hide
25137      */
25138     /**
25139      * @cfg {String} autoCreate @hide
25140      */
25141     /**
25142      * @cfg {String} inputType @hide
25143      */
25144      
25145     /**
25146      * @cfg {String} invalidText @hide
25147      */
25148     /**
25149      * @cfg {String} msgFx @hide
25150      */
25151     /**
25152      * @cfg {String} validateOnBlur @hide
25153      */
25154 });
25155  
25156     
25157    
25158    
25159    
25160       
25161 Roo.namespace('Roo.bootstrap.htmleditor');
25162 /**
25163  * @class Roo.bootstrap.HtmlEditorToolbar1
25164  * Basic Toolbar
25165  * 
25166  * @example
25167  * Usage:
25168  *
25169  new Roo.bootstrap.HtmlEditor({
25170     ....
25171     toolbars : [
25172         new Roo.bootstrap.HtmlEditorToolbar1({
25173             disable : { fonts: 1 , format: 1, ..., ... , ...],
25174             btns : [ .... ]
25175         })
25176     }
25177      
25178  * 
25179  * @cfg {Object} disable List of elements to disable..
25180  * @cfg {Array} btns List of additional buttons.
25181  * 
25182  * 
25183  * NEEDS Extra CSS? 
25184  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25185  */
25186  
25187 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25188 {
25189     
25190     Roo.apply(this, config);
25191     
25192     // default disabled, based on 'good practice'..
25193     this.disable = this.disable || {};
25194     Roo.applyIf(this.disable, {
25195         fontSize : true,
25196         colors : true,
25197         specialElements : true
25198     });
25199     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25200     
25201     this.editor = config.editor;
25202     this.editorcore = config.editor.editorcore;
25203     
25204     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25205     
25206     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25207     // dont call parent... till later.
25208 }
25209 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25210      
25211     bar : true,
25212     
25213     editor : false,
25214     editorcore : false,
25215     
25216     
25217     formats : [
25218         "p" ,  
25219         "h1","h2","h3","h4","h5","h6", 
25220         "pre", "code", 
25221         "abbr", "acronym", "address", "cite", "samp", "var",
25222         'div','span'
25223     ],
25224     
25225     onRender : function(ct, position)
25226     {
25227        // Roo.log("Call onRender: " + this.xtype);
25228         
25229        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25230        Roo.log(this.el);
25231        this.el.dom.style.marginBottom = '0';
25232        var _this = this;
25233        var editorcore = this.editorcore;
25234        var editor= this.editor;
25235        
25236        var children = [];
25237        var btn = function(id,cmd , toggle, handler, html){
25238        
25239             var  event = toggle ? 'toggle' : 'click';
25240        
25241             var a = {
25242                 size : 'sm',
25243                 xtype: 'Button',
25244                 xns: Roo.bootstrap,
25245                 //glyphicon : id,
25246                 fa: id,
25247                 cmd : id || cmd,
25248                 enableToggle:toggle !== false,
25249                 html : html || '',
25250                 pressed : toggle ? false : null,
25251                 listeners : {}
25252             };
25253             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25254                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25255             };
25256             children.push(a);
25257             return a;
25258        }
25259        
25260     //    var cb_box = function...
25261         
25262         var style = {
25263                 xtype: 'Button',
25264                 size : 'sm',
25265                 xns: Roo.bootstrap,
25266                 fa : 'font',
25267                 //html : 'submit'
25268                 menu : {
25269                     xtype: 'Menu',
25270                     xns: Roo.bootstrap,
25271                     items:  []
25272                 }
25273         };
25274         Roo.each(this.formats, function(f) {
25275             style.menu.items.push({
25276                 xtype :'MenuItem',
25277                 xns: Roo.bootstrap,
25278                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25279                 tagname : f,
25280                 listeners : {
25281                     click : function()
25282                     {
25283                         editorcore.insertTag(this.tagname);
25284                         editor.focus();
25285                     }
25286                 }
25287                 
25288             });
25289         });
25290         children.push(style);   
25291         
25292         btn('bold',false,true);
25293         btn('italic',false,true);
25294         btn('align-left', 'justifyleft',true);
25295         btn('align-center', 'justifycenter',true);
25296         btn('align-right' , 'justifyright',true);
25297         btn('link', false, false, function(btn) {
25298             //Roo.log("create link?");
25299             var url = prompt(this.createLinkText, this.defaultLinkValue);
25300             if(url && url != 'http:/'+'/'){
25301                 this.editorcore.relayCmd('createlink', url);
25302             }
25303         }),
25304         btn('list','insertunorderedlist',true);
25305         btn('pencil', false,true, function(btn){
25306                 Roo.log(this);
25307                 this.toggleSourceEdit(btn.pressed);
25308         });
25309         
25310         if (this.editor.btns.length > 0) {
25311             for (var i = 0; i<this.editor.btns.length; i++) {
25312                 children.push(this.editor.btns[i]);
25313             }
25314         }
25315         
25316         /*
25317         var cog = {
25318                 xtype: 'Button',
25319                 size : 'sm',
25320                 xns: Roo.bootstrap,
25321                 glyphicon : 'cog',
25322                 //html : 'submit'
25323                 menu : {
25324                     xtype: 'Menu',
25325                     xns: Roo.bootstrap,
25326                     items:  []
25327                 }
25328         };
25329         
25330         cog.menu.items.push({
25331             xtype :'MenuItem',
25332             xns: Roo.bootstrap,
25333             html : Clean styles,
25334             tagname : f,
25335             listeners : {
25336                 click : function()
25337                 {
25338                     editorcore.insertTag(this.tagname);
25339                     editor.focus();
25340                 }
25341             }
25342             
25343         });
25344        */
25345         
25346          
25347        this.xtype = 'NavSimplebar';
25348         
25349         for(var i=0;i< children.length;i++) {
25350             
25351             this.buttons.add(this.addxtypeChild(children[i]));
25352             
25353         }
25354         
25355         editor.on('editorevent', this.updateToolbar, this);
25356     },
25357     onBtnClick : function(id)
25358     {
25359        this.editorcore.relayCmd(id);
25360        this.editorcore.focus();
25361     },
25362     
25363     /**
25364      * Protected method that will not generally be called directly. It triggers
25365      * a toolbar update by reading the markup state of the current selection in the editor.
25366      */
25367     updateToolbar: function(){
25368
25369         if(!this.editorcore.activated){
25370             this.editor.onFirstFocus(); // is this neeed?
25371             return;
25372         }
25373
25374         var btns = this.buttons; 
25375         var doc = this.editorcore.doc;
25376         btns.get('bold').setActive(doc.queryCommandState('bold'));
25377         btns.get('italic').setActive(doc.queryCommandState('italic'));
25378         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25379         
25380         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25381         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25382         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25383         
25384         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25385         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25386          /*
25387         
25388         var ans = this.editorcore.getAllAncestors();
25389         if (this.formatCombo) {
25390             
25391             
25392             var store = this.formatCombo.store;
25393             this.formatCombo.setValue("");
25394             for (var i =0; i < ans.length;i++) {
25395                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25396                     // select it..
25397                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25398                     break;
25399                 }
25400             }
25401         }
25402         
25403         
25404         
25405         // hides menus... - so this cant be on a menu...
25406         Roo.bootstrap.MenuMgr.hideAll();
25407         */
25408         Roo.bootstrap.MenuMgr.hideAll();
25409         //this.editorsyncValue();
25410     },
25411     onFirstFocus: function() {
25412         this.buttons.each(function(item){
25413            item.enable();
25414         });
25415     },
25416     toggleSourceEdit : function(sourceEditMode){
25417         
25418           
25419         if(sourceEditMode){
25420             Roo.log("disabling buttons");
25421            this.buttons.each( function(item){
25422                 if(item.cmd != 'pencil'){
25423                     item.disable();
25424                 }
25425             });
25426           
25427         }else{
25428             Roo.log("enabling buttons");
25429             if(this.editorcore.initialized){
25430                 this.buttons.each( function(item){
25431                     item.enable();
25432                 });
25433             }
25434             
25435         }
25436         Roo.log("calling toggole on editor");
25437         // tell the editor that it's been pressed..
25438         this.editor.toggleSourceEdit(sourceEditMode);
25439        
25440     }
25441 });
25442
25443
25444
25445
25446
25447 /**
25448  * @class Roo.bootstrap.Table.AbstractSelectionModel
25449  * @extends Roo.util.Observable
25450  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25451  * implemented by descendant classes.  This class should not be directly instantiated.
25452  * @constructor
25453  */
25454 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25455     this.locked = false;
25456     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25457 };
25458
25459
25460 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25461     /** @ignore Called by the grid automatically. Do not call directly. */
25462     init : function(grid){
25463         this.grid = grid;
25464         this.initEvents();
25465     },
25466
25467     /**
25468      * Locks the selections.
25469      */
25470     lock : function(){
25471         this.locked = true;
25472     },
25473
25474     /**
25475      * Unlocks the selections.
25476      */
25477     unlock : function(){
25478         this.locked = false;
25479     },
25480
25481     /**
25482      * Returns true if the selections are locked.
25483      * @return {Boolean}
25484      */
25485     isLocked : function(){
25486         return this.locked;
25487     },
25488     
25489     
25490     initEvents : function ()
25491     {
25492         
25493     }
25494 });
25495 /**
25496  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25497  * @class Roo.bootstrap.Table.RowSelectionModel
25498  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25499  * It supports multiple selections and keyboard selection/navigation. 
25500  * @constructor
25501  * @param {Object} config
25502  */
25503
25504 Roo.bootstrap.Table.RowSelectionModel = function(config){
25505     Roo.apply(this, config);
25506     this.selections = new Roo.util.MixedCollection(false, function(o){
25507         return o.id;
25508     });
25509
25510     this.last = false;
25511     this.lastActive = false;
25512
25513     this.addEvents({
25514         /**
25515              * @event selectionchange
25516              * Fires when the selection changes
25517              * @param {SelectionModel} this
25518              */
25519             "selectionchange" : true,
25520         /**
25521              * @event afterselectionchange
25522              * Fires after the selection changes (eg. by key press or clicking)
25523              * @param {SelectionModel} this
25524              */
25525             "afterselectionchange" : true,
25526         /**
25527              * @event beforerowselect
25528              * Fires when a row is selected being selected, return false to cancel.
25529              * @param {SelectionModel} this
25530              * @param {Number} rowIndex The selected index
25531              * @param {Boolean} keepExisting False if other selections will be cleared
25532              */
25533             "beforerowselect" : true,
25534         /**
25535              * @event rowselect
25536              * Fires when a row is selected.
25537              * @param {SelectionModel} this
25538              * @param {Number} rowIndex The selected index
25539              * @param {Roo.data.Record} r The record
25540              */
25541             "rowselect" : true,
25542         /**
25543              * @event rowdeselect
25544              * Fires when a row is deselected.
25545              * @param {SelectionModel} this
25546              * @param {Number} rowIndex The selected index
25547              */
25548         "rowdeselect" : true
25549     });
25550     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25551     this.locked = false;
25552  };
25553
25554 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25555     /**
25556      * @cfg {Boolean} singleSelect
25557      * True to allow selection of only one row at a time (defaults to false)
25558      */
25559     singleSelect : false,
25560
25561     // private
25562     initEvents : function()
25563     {
25564
25565         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25566         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25567         //}else{ // allow click to work like normal
25568          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25569         //}
25570         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25571         this.grid.on("rowclick", this.handleMouseDown, this);
25572         
25573         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25574             "up" : function(e){
25575                 if(!e.shiftKey){
25576                     this.selectPrevious(e.shiftKey);
25577                 }else if(this.last !== false && this.lastActive !== false){
25578                     var last = this.last;
25579                     this.selectRange(this.last,  this.lastActive-1);
25580                     this.grid.getView().focusRow(this.lastActive);
25581                     if(last !== false){
25582                         this.last = last;
25583                     }
25584                 }else{
25585                     this.selectFirstRow();
25586                 }
25587                 this.fireEvent("afterselectionchange", this);
25588             },
25589             "down" : function(e){
25590                 if(!e.shiftKey){
25591                     this.selectNext(e.shiftKey);
25592                 }else if(this.last !== false && this.lastActive !== false){
25593                     var last = this.last;
25594                     this.selectRange(this.last,  this.lastActive+1);
25595                     this.grid.getView().focusRow(this.lastActive);
25596                     if(last !== false){
25597                         this.last = last;
25598                     }
25599                 }else{
25600                     this.selectFirstRow();
25601                 }
25602                 this.fireEvent("afterselectionchange", this);
25603             },
25604             scope: this
25605         });
25606         this.grid.store.on('load', function(){
25607             this.selections.clear();
25608         },this);
25609         /*
25610         var view = this.grid.view;
25611         view.on("refresh", this.onRefresh, this);
25612         view.on("rowupdated", this.onRowUpdated, this);
25613         view.on("rowremoved", this.onRemove, this);
25614         */
25615     },
25616
25617     // private
25618     onRefresh : function()
25619     {
25620         var ds = this.grid.store, i, v = this.grid.view;
25621         var s = this.selections;
25622         s.each(function(r){
25623             if((i = ds.indexOfId(r.id)) != -1){
25624                 v.onRowSelect(i);
25625             }else{
25626                 s.remove(r);
25627             }
25628         });
25629     },
25630
25631     // private
25632     onRemove : function(v, index, r){
25633         this.selections.remove(r);
25634     },
25635
25636     // private
25637     onRowUpdated : function(v, index, r){
25638         if(this.isSelected(r)){
25639             v.onRowSelect(index);
25640         }
25641     },
25642
25643     /**
25644      * Select records.
25645      * @param {Array} records The records to select
25646      * @param {Boolean} keepExisting (optional) True to keep existing selections
25647      */
25648     selectRecords : function(records, keepExisting)
25649     {
25650         if(!keepExisting){
25651             this.clearSelections();
25652         }
25653             var ds = this.grid.store;
25654         for(var i = 0, len = records.length; i < len; i++){
25655             this.selectRow(ds.indexOf(records[i]), true);
25656         }
25657     },
25658
25659     /**
25660      * Gets the number of selected rows.
25661      * @return {Number}
25662      */
25663     getCount : function(){
25664         return this.selections.length;
25665     },
25666
25667     /**
25668      * Selects the first row in the grid.
25669      */
25670     selectFirstRow : function(){
25671         this.selectRow(0);
25672     },
25673
25674     /**
25675      * Select the last row.
25676      * @param {Boolean} keepExisting (optional) True to keep existing selections
25677      */
25678     selectLastRow : function(keepExisting){
25679         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25680         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25681     },
25682
25683     /**
25684      * Selects the row immediately following the last selected row.
25685      * @param {Boolean} keepExisting (optional) True to keep existing selections
25686      */
25687     selectNext : function(keepExisting)
25688     {
25689             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25690             this.selectRow(this.last+1, keepExisting);
25691             this.grid.getView().focusRow(this.last);
25692         }
25693     },
25694
25695     /**
25696      * Selects the row that precedes the last selected row.
25697      * @param {Boolean} keepExisting (optional) True to keep existing selections
25698      */
25699     selectPrevious : function(keepExisting){
25700         if(this.last){
25701             this.selectRow(this.last-1, keepExisting);
25702             this.grid.getView().focusRow(this.last);
25703         }
25704     },
25705
25706     /**
25707      * Returns the selected records
25708      * @return {Array} Array of selected records
25709      */
25710     getSelections : function(){
25711         return [].concat(this.selections.items);
25712     },
25713
25714     /**
25715      * Returns the first selected record.
25716      * @return {Record}
25717      */
25718     getSelected : function(){
25719         return this.selections.itemAt(0);
25720     },
25721
25722
25723     /**
25724      * Clears all selections.
25725      */
25726     clearSelections : function(fast)
25727     {
25728         if(this.locked) {
25729             return;
25730         }
25731         if(fast !== true){
25732                 var ds = this.grid.store;
25733             var s = this.selections;
25734             s.each(function(r){
25735                 this.deselectRow(ds.indexOfId(r.id));
25736             }, this);
25737             s.clear();
25738         }else{
25739             this.selections.clear();
25740         }
25741         this.last = false;
25742     },
25743
25744
25745     /**
25746      * Selects all rows.
25747      */
25748     selectAll : function(){
25749         if(this.locked) {
25750             return;
25751         }
25752         this.selections.clear();
25753         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25754             this.selectRow(i, true);
25755         }
25756     },
25757
25758     /**
25759      * Returns True if there is a selection.
25760      * @return {Boolean}
25761      */
25762     hasSelection : function(){
25763         return this.selections.length > 0;
25764     },
25765
25766     /**
25767      * Returns True if the specified row is selected.
25768      * @param {Number/Record} record The record or index of the record to check
25769      * @return {Boolean}
25770      */
25771     isSelected : function(index){
25772             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25773         return (r && this.selections.key(r.id) ? true : false);
25774     },
25775
25776     /**
25777      * Returns True if the specified record id is selected.
25778      * @param {String} id The id of record to check
25779      * @return {Boolean}
25780      */
25781     isIdSelected : function(id){
25782         return (this.selections.key(id) ? true : false);
25783     },
25784
25785
25786     // private
25787     handleMouseDBClick : function(e, t){
25788         
25789     },
25790     // private
25791     handleMouseDown : function(e, t)
25792     {
25793             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25794         if(this.isLocked() || rowIndex < 0 ){
25795             return;
25796         };
25797         if(e.shiftKey && this.last !== false){
25798             var last = this.last;
25799             this.selectRange(last, rowIndex, e.ctrlKey);
25800             this.last = last; // reset the last
25801             t.focus();
25802     
25803         }else{
25804             var isSelected = this.isSelected(rowIndex);
25805             //Roo.log("select row:" + rowIndex);
25806             if(isSelected){
25807                 this.deselectRow(rowIndex);
25808             } else {
25809                         this.selectRow(rowIndex, true);
25810             }
25811     
25812             /*
25813                 if(e.button !== 0 && isSelected){
25814                 alert('rowIndex 2: ' + rowIndex);
25815                     view.focusRow(rowIndex);
25816                 }else if(e.ctrlKey && isSelected){
25817                     this.deselectRow(rowIndex);
25818                 }else if(!isSelected){
25819                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25820                     view.focusRow(rowIndex);
25821                 }
25822             */
25823         }
25824         this.fireEvent("afterselectionchange", this);
25825     },
25826     // private
25827     handleDragableRowClick :  function(grid, rowIndex, e) 
25828     {
25829         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25830             this.selectRow(rowIndex, false);
25831             grid.view.focusRow(rowIndex);
25832              this.fireEvent("afterselectionchange", this);
25833         }
25834     },
25835     
25836     /**
25837      * Selects multiple rows.
25838      * @param {Array} rows Array of the indexes of the row to select
25839      * @param {Boolean} keepExisting (optional) True to keep existing selections
25840      */
25841     selectRows : function(rows, keepExisting){
25842         if(!keepExisting){
25843             this.clearSelections();
25844         }
25845         for(var i = 0, len = rows.length; i < len; i++){
25846             this.selectRow(rows[i], true);
25847         }
25848     },
25849
25850     /**
25851      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25852      * @param {Number} startRow The index of the first row in the range
25853      * @param {Number} endRow The index of the last row in the range
25854      * @param {Boolean} keepExisting (optional) True to retain existing selections
25855      */
25856     selectRange : function(startRow, endRow, keepExisting){
25857         if(this.locked) {
25858             return;
25859         }
25860         if(!keepExisting){
25861             this.clearSelections();
25862         }
25863         if(startRow <= endRow){
25864             for(var i = startRow; i <= endRow; i++){
25865                 this.selectRow(i, true);
25866             }
25867         }else{
25868             for(var i = startRow; i >= endRow; i--){
25869                 this.selectRow(i, true);
25870             }
25871         }
25872     },
25873
25874     /**
25875      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25876      * @param {Number} startRow The index of the first row in the range
25877      * @param {Number} endRow The index of the last row in the range
25878      */
25879     deselectRange : function(startRow, endRow, preventViewNotify){
25880         if(this.locked) {
25881             return;
25882         }
25883         for(var i = startRow; i <= endRow; i++){
25884             this.deselectRow(i, preventViewNotify);
25885         }
25886     },
25887
25888     /**
25889      * Selects a row.
25890      * @param {Number} row The index of the row to select
25891      * @param {Boolean} keepExisting (optional) True to keep existing selections
25892      */
25893     selectRow : function(index, keepExisting, preventViewNotify)
25894     {
25895             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25896             return;
25897         }
25898         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25899             if(!keepExisting || this.singleSelect){
25900                 this.clearSelections();
25901             }
25902             
25903             var r = this.grid.store.getAt(index);
25904             //console.log('selectRow - record id :' + r.id);
25905             
25906             this.selections.add(r);
25907             this.last = this.lastActive = index;
25908             if(!preventViewNotify){
25909                 var proxy = new Roo.Element(
25910                                 this.grid.getRowDom(index)
25911                 );
25912                 proxy.addClass('bg-info info');
25913             }
25914             this.fireEvent("rowselect", this, index, r);
25915             this.fireEvent("selectionchange", this);
25916         }
25917     },
25918
25919     /**
25920      * Deselects a row.
25921      * @param {Number} row The index of the row to deselect
25922      */
25923     deselectRow : function(index, preventViewNotify)
25924     {
25925         if(this.locked) {
25926             return;
25927         }
25928         if(this.last == index){
25929             this.last = false;
25930         }
25931         if(this.lastActive == index){
25932             this.lastActive = false;
25933         }
25934         
25935         var r = this.grid.store.getAt(index);
25936         if (!r) {
25937             return;
25938         }
25939         
25940         this.selections.remove(r);
25941         //.console.log('deselectRow - record id :' + r.id);
25942         if(!preventViewNotify){
25943         
25944             var proxy = new Roo.Element(
25945                 this.grid.getRowDom(index)
25946             );
25947             proxy.removeClass('bg-info info');
25948         }
25949         this.fireEvent("rowdeselect", this, index);
25950         this.fireEvent("selectionchange", this);
25951     },
25952
25953     // private
25954     restoreLast : function(){
25955         if(this._last){
25956             this.last = this._last;
25957         }
25958     },
25959
25960     // private
25961     acceptsNav : function(row, col, cm){
25962         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25963     },
25964
25965     // private
25966     onEditorKey : function(field, e){
25967         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25968         if(k == e.TAB){
25969             e.stopEvent();
25970             ed.completeEdit();
25971             if(e.shiftKey){
25972                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25973             }else{
25974                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25975             }
25976         }else if(k == e.ENTER && !e.ctrlKey){
25977             e.stopEvent();
25978             ed.completeEdit();
25979             if(e.shiftKey){
25980                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25981             }else{
25982                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25983             }
25984         }else if(k == e.ESC){
25985             ed.cancelEdit();
25986         }
25987         if(newCell){
25988             g.startEditing(newCell[0], newCell[1]);
25989         }
25990     }
25991 });
25992 /*
25993  * Based on:
25994  * Ext JS Library 1.1.1
25995  * Copyright(c) 2006-2007, Ext JS, LLC.
25996  *
25997  * Originally Released Under LGPL - original licence link has changed is not relivant.
25998  *
25999  * Fork - LGPL
26000  * <script type="text/javascript">
26001  */
26002  
26003 /**
26004  * @class Roo.bootstrap.PagingToolbar
26005  * @extends Roo.bootstrap.NavSimplebar
26006  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26007  * @constructor
26008  * Create a new PagingToolbar
26009  * @param {Object} config The config object
26010  * @param {Roo.data.Store} store
26011  */
26012 Roo.bootstrap.PagingToolbar = function(config)
26013 {
26014     // old args format still supported... - xtype is prefered..
26015         // created from xtype...
26016     
26017     this.ds = config.dataSource;
26018     
26019     if (config.store && !this.ds) {
26020         this.store= Roo.factory(config.store, Roo.data);
26021         this.ds = this.store;
26022         this.ds.xmodule = this.xmodule || false;
26023     }
26024     
26025     this.toolbarItems = [];
26026     if (config.items) {
26027         this.toolbarItems = config.items;
26028     }
26029     
26030     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26031     
26032     this.cursor = 0;
26033     
26034     if (this.ds) { 
26035         this.bind(this.ds);
26036     }
26037     
26038     if (Roo.bootstrap.version == 4) {
26039         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26040     } else {
26041         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26042     }
26043     
26044 };
26045
26046 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26047     /**
26048      * @cfg {Roo.data.Store} dataSource
26049      * The underlying data store providing the paged data
26050      */
26051     /**
26052      * @cfg {String/HTMLElement/Element} container
26053      * container The id or element that will contain the toolbar
26054      */
26055     /**
26056      * @cfg {Boolean} displayInfo
26057      * True to display the displayMsg (defaults to false)
26058      */
26059     /**
26060      * @cfg {Number} pageSize
26061      * The number of records to display per page (defaults to 20)
26062      */
26063     pageSize: 20,
26064     /**
26065      * @cfg {String} displayMsg
26066      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26067      */
26068     displayMsg : 'Displaying {0} - {1} of {2}',
26069     /**
26070      * @cfg {String} emptyMsg
26071      * The message to display when no records are found (defaults to "No data to display")
26072      */
26073     emptyMsg : 'No data to display',
26074     /**
26075      * Customizable piece of the default paging text (defaults to "Page")
26076      * @type String
26077      */
26078     beforePageText : "Page",
26079     /**
26080      * Customizable piece of the default paging text (defaults to "of %0")
26081      * @type String
26082      */
26083     afterPageText : "of {0}",
26084     /**
26085      * Customizable piece of the default paging text (defaults to "First Page")
26086      * @type String
26087      */
26088     firstText : "First Page",
26089     /**
26090      * Customizable piece of the default paging text (defaults to "Previous Page")
26091      * @type String
26092      */
26093     prevText : "Previous Page",
26094     /**
26095      * Customizable piece of the default paging text (defaults to "Next Page")
26096      * @type String
26097      */
26098     nextText : "Next Page",
26099     /**
26100      * Customizable piece of the default paging text (defaults to "Last Page")
26101      * @type String
26102      */
26103     lastText : "Last Page",
26104     /**
26105      * Customizable piece of the default paging text (defaults to "Refresh")
26106      * @type String
26107      */
26108     refreshText : "Refresh",
26109
26110     buttons : false,
26111     // private
26112     onRender : function(ct, position) 
26113     {
26114         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26115         this.navgroup.parentId = this.id;
26116         this.navgroup.onRender(this.el, null);
26117         // add the buttons to the navgroup
26118         
26119         if(this.displayInfo){
26120             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26121             this.displayEl = this.el.select('.x-paging-info', true).first();
26122 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26123 //            this.displayEl = navel.el.select('span',true).first();
26124         }
26125         
26126         var _this = this;
26127         
26128         if(this.buttons){
26129             Roo.each(_this.buttons, function(e){ // this might need to use render????
26130                Roo.factory(e).render(_this.el);
26131             });
26132         }
26133             
26134         Roo.each(_this.toolbarItems, function(e) {
26135             _this.navgroup.addItem(e);
26136         });
26137         
26138         
26139         this.first = this.navgroup.addItem({
26140             tooltip: this.firstText,
26141             cls: "prev btn-outline-secondary",
26142             html : ' <i class="fa fa-step-backward"></i>',
26143             disabled: true,
26144             preventDefault: true,
26145             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26146         });
26147         
26148         this.prev =  this.navgroup.addItem({
26149             tooltip: this.prevText,
26150             cls: "prev btn-outline-secondary",
26151             html : ' <i class="fa fa-backward"></i>',
26152             disabled: true,
26153             preventDefault: true,
26154             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26155         });
26156     //this.addSeparator();
26157         
26158         
26159         var field = this.navgroup.addItem( {
26160             tagtype : 'span',
26161             cls : 'x-paging-position  btn-outline-secondary',
26162              disabled: true,
26163             html : this.beforePageText  +
26164                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26165                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26166          } ); //?? escaped?
26167         
26168         this.field = field.el.select('input', true).first();
26169         this.field.on("keydown", this.onPagingKeydown, this);
26170         this.field.on("focus", function(){this.dom.select();});
26171     
26172     
26173         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26174         //this.field.setHeight(18);
26175         //this.addSeparator();
26176         this.next = this.navgroup.addItem({
26177             tooltip: this.nextText,
26178             cls: "next btn-outline-secondary",
26179             html : ' <i class="fa fa-forward"></i>',
26180             disabled: true,
26181             preventDefault: true,
26182             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26183         });
26184         this.last = this.navgroup.addItem({
26185             tooltip: this.lastText,
26186             html : ' <i class="fa fa-step-forward"></i>',
26187             cls: "next btn-outline-secondary",
26188             disabled: true,
26189             preventDefault: true,
26190             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26191         });
26192     //this.addSeparator();
26193         this.loading = this.navgroup.addItem({
26194             tooltip: this.refreshText,
26195             cls: "btn-outline-secondary",
26196             html : ' <i class="fa fa-refresh"></i>',
26197             preventDefault: true,
26198             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26199         });
26200         
26201     },
26202
26203     // private
26204     updateInfo : function(){
26205         if(this.displayEl){
26206             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26207             var msg = count == 0 ?
26208                 this.emptyMsg :
26209                 String.format(
26210                     this.displayMsg,
26211                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26212                 );
26213             this.displayEl.update(msg);
26214         }
26215     },
26216
26217     // private
26218     onLoad : function(ds, r, o)
26219     {
26220         this.cursor = o.params.start ? o.params.start : 0;
26221         
26222         var d = this.getPageData(),
26223             ap = d.activePage,
26224             ps = d.pages;
26225         
26226         
26227         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26228         this.field.dom.value = ap;
26229         this.first.setDisabled(ap == 1);
26230         this.prev.setDisabled(ap == 1);
26231         this.next.setDisabled(ap == ps);
26232         this.last.setDisabled(ap == ps);
26233         this.loading.enable();
26234         this.updateInfo();
26235     },
26236
26237     // private
26238     getPageData : function(){
26239         var total = this.ds.getTotalCount();
26240         return {
26241             total : total,
26242             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26243             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26244         };
26245     },
26246
26247     // private
26248     onLoadError : function(){
26249         this.loading.enable();
26250     },
26251
26252     // private
26253     onPagingKeydown : function(e){
26254         var k = e.getKey();
26255         var d = this.getPageData();
26256         if(k == e.RETURN){
26257             var v = this.field.dom.value, pageNum;
26258             if(!v || isNaN(pageNum = parseInt(v, 10))){
26259                 this.field.dom.value = d.activePage;
26260                 return;
26261             }
26262             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26263             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26264             e.stopEvent();
26265         }
26266         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))
26267         {
26268           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26269           this.field.dom.value = pageNum;
26270           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26271           e.stopEvent();
26272         }
26273         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26274         {
26275           var v = this.field.dom.value, pageNum; 
26276           var increment = (e.shiftKey) ? 10 : 1;
26277           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26278                 increment *= -1;
26279           }
26280           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26281             this.field.dom.value = d.activePage;
26282             return;
26283           }
26284           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26285           {
26286             this.field.dom.value = parseInt(v, 10) + increment;
26287             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26288             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26289           }
26290           e.stopEvent();
26291         }
26292     },
26293
26294     // private
26295     beforeLoad : function(){
26296         if(this.loading){
26297             this.loading.disable();
26298         }
26299     },
26300
26301     // private
26302     onClick : function(which){
26303         
26304         var ds = this.ds;
26305         if (!ds) {
26306             return;
26307         }
26308         
26309         switch(which){
26310             case "first":
26311                 ds.load({params:{start: 0, limit: this.pageSize}});
26312             break;
26313             case "prev":
26314                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26315             break;
26316             case "next":
26317                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26318             break;
26319             case "last":
26320                 var total = ds.getTotalCount();
26321                 var extra = total % this.pageSize;
26322                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26323                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26324             break;
26325             case "refresh":
26326                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26327             break;
26328         }
26329     },
26330
26331     /**
26332      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26333      * @param {Roo.data.Store} store The data store to unbind
26334      */
26335     unbind : function(ds){
26336         ds.un("beforeload", this.beforeLoad, this);
26337         ds.un("load", this.onLoad, this);
26338         ds.un("loadexception", this.onLoadError, this);
26339         ds.un("remove", this.updateInfo, this);
26340         ds.un("add", this.updateInfo, this);
26341         this.ds = undefined;
26342     },
26343
26344     /**
26345      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26346      * @param {Roo.data.Store} store The data store to bind
26347      */
26348     bind : function(ds){
26349         ds.on("beforeload", this.beforeLoad, this);
26350         ds.on("load", this.onLoad, this);
26351         ds.on("loadexception", this.onLoadError, this);
26352         ds.on("remove", this.updateInfo, this);
26353         ds.on("add", this.updateInfo, this);
26354         this.ds = ds;
26355     }
26356 });/*
26357  * - LGPL
26358  *
26359  * element
26360  * 
26361  */
26362
26363 /**
26364  * @class Roo.bootstrap.MessageBar
26365  * @extends Roo.bootstrap.Component
26366  * Bootstrap MessageBar class
26367  * @cfg {String} html contents of the MessageBar
26368  * @cfg {String} weight (info | success | warning | danger) default info
26369  * @cfg {String} beforeClass insert the bar before the given class
26370  * @cfg {Boolean} closable (true | false) default false
26371  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26372  * 
26373  * @constructor
26374  * Create a new Element
26375  * @param {Object} config The config object
26376  */
26377
26378 Roo.bootstrap.MessageBar = function(config){
26379     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26380 };
26381
26382 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26383     
26384     html: '',
26385     weight: 'info',
26386     closable: false,
26387     fixed: false,
26388     beforeClass: 'bootstrap-sticky-wrap',
26389     
26390     getAutoCreate : function(){
26391         
26392         var cfg = {
26393             tag: 'div',
26394             cls: 'alert alert-dismissable alert-' + this.weight,
26395             cn: [
26396                 {
26397                     tag: 'span',
26398                     cls: 'message',
26399                     html: this.html || ''
26400                 }
26401             ]
26402         };
26403         
26404         if(this.fixed){
26405             cfg.cls += ' alert-messages-fixed';
26406         }
26407         
26408         if(this.closable){
26409             cfg.cn.push({
26410                 tag: 'button',
26411                 cls: 'close',
26412                 html: 'x'
26413             });
26414         }
26415         
26416         return cfg;
26417     },
26418     
26419     onRender : function(ct, position)
26420     {
26421         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26422         
26423         if(!this.el){
26424             var cfg = Roo.apply({},  this.getAutoCreate());
26425             cfg.id = Roo.id();
26426             
26427             if (this.cls) {
26428                 cfg.cls += ' ' + this.cls;
26429             }
26430             if (this.style) {
26431                 cfg.style = this.style;
26432             }
26433             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26434             
26435             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26436         }
26437         
26438         this.el.select('>button.close').on('click', this.hide, this);
26439         
26440     },
26441     
26442     show : function()
26443     {
26444         if (!this.rendered) {
26445             this.render();
26446         }
26447         
26448         this.el.show();
26449         
26450         this.fireEvent('show', this);
26451         
26452     },
26453     
26454     hide : function()
26455     {
26456         if (!this.rendered) {
26457             this.render();
26458         }
26459         
26460         this.el.hide();
26461         
26462         this.fireEvent('hide', this);
26463     },
26464     
26465     update : function()
26466     {
26467 //        var e = this.el.dom.firstChild;
26468 //        
26469 //        if(this.closable){
26470 //            e = e.nextSibling;
26471 //        }
26472 //        
26473 //        e.data = this.html || '';
26474
26475         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26476     }
26477    
26478 });
26479
26480  
26481
26482      /*
26483  * - LGPL
26484  *
26485  * Graph
26486  * 
26487  */
26488
26489
26490 /**
26491  * @class Roo.bootstrap.Graph
26492  * @extends Roo.bootstrap.Component
26493  * Bootstrap Graph class
26494 > Prameters
26495  -sm {number} sm 4
26496  -md {number} md 5
26497  @cfg {String} graphtype  bar | vbar | pie
26498  @cfg {number} g_x coodinator | centre x (pie)
26499  @cfg {number} g_y coodinator | centre y (pie)
26500  @cfg {number} g_r radius (pie)
26501  @cfg {number} g_height height of the chart (respected by all elements in the set)
26502  @cfg {number} g_width width of the chart (respected by all elements in the set)
26503  @cfg {Object} title The title of the chart
26504     
26505  -{Array}  values
26506  -opts (object) options for the chart 
26507      o {
26508      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26509      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26510      o vgutter (number)
26511      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.
26512      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26513      o to
26514      o stretch (boolean)
26515      o }
26516  -opts (object) options for the pie
26517      o{
26518      o cut
26519      o startAngle (number)
26520      o endAngle (number)
26521      } 
26522  *
26523  * @constructor
26524  * Create a new Input
26525  * @param {Object} config The config object
26526  */
26527
26528 Roo.bootstrap.Graph = function(config){
26529     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26530     
26531     this.addEvents({
26532         // img events
26533         /**
26534          * @event click
26535          * The img click event for the img.
26536          * @param {Roo.EventObject} e
26537          */
26538         "click" : true
26539     });
26540 };
26541
26542 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26543     
26544     sm: 4,
26545     md: 5,
26546     graphtype: 'bar',
26547     g_height: 250,
26548     g_width: 400,
26549     g_x: 50,
26550     g_y: 50,
26551     g_r: 30,
26552     opts:{
26553         //g_colors: this.colors,
26554         g_type: 'soft',
26555         g_gutter: '20%'
26556
26557     },
26558     title : false,
26559
26560     getAutoCreate : function(){
26561         
26562         var cfg = {
26563             tag: 'div',
26564             html : null
26565         };
26566         
26567         
26568         return  cfg;
26569     },
26570
26571     onRender : function(ct,position){
26572         
26573         
26574         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26575         
26576         if (typeof(Raphael) == 'undefined') {
26577             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26578             return;
26579         }
26580         
26581         this.raphael = Raphael(this.el.dom);
26582         
26583                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26584                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26585                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26586                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26587                 /*
26588                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26589                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26590                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26591                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26592                 
26593                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26594                 r.barchart(330, 10, 300, 220, data1);
26595                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26596                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26597                 */
26598                 
26599                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26600                 // r.barchart(30, 30, 560, 250,  xdata, {
26601                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26602                 //     axis : "0 0 1 1",
26603                 //     axisxlabels :  xdata
26604                 //     //yvalues : cols,
26605                    
26606                 // });
26607 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26608 //        
26609 //        this.load(null,xdata,{
26610 //                axis : "0 0 1 1",
26611 //                axisxlabels :  xdata
26612 //                });
26613
26614     },
26615
26616     load : function(graphtype,xdata,opts)
26617     {
26618         this.raphael.clear();
26619         if(!graphtype) {
26620             graphtype = this.graphtype;
26621         }
26622         if(!opts){
26623             opts = this.opts;
26624         }
26625         var r = this.raphael,
26626             fin = function () {
26627                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26628             },
26629             fout = function () {
26630                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26631             },
26632             pfin = function() {
26633                 this.sector.stop();
26634                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26635
26636                 if (this.label) {
26637                     this.label[0].stop();
26638                     this.label[0].attr({ r: 7.5 });
26639                     this.label[1].attr({ "font-weight": 800 });
26640                 }
26641             },
26642             pfout = function() {
26643                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26644
26645                 if (this.label) {
26646                     this.label[0].animate({ r: 5 }, 500, "bounce");
26647                     this.label[1].attr({ "font-weight": 400 });
26648                 }
26649             };
26650
26651         switch(graphtype){
26652             case 'bar':
26653                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26654                 break;
26655             case 'hbar':
26656                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26657                 break;
26658             case 'pie':
26659 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26660 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26661 //            
26662                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26663                 
26664                 break;
26665
26666         }
26667         
26668         if(this.title){
26669             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26670         }
26671         
26672     },
26673     
26674     setTitle: function(o)
26675     {
26676         this.title = o;
26677     },
26678     
26679     initEvents: function() {
26680         
26681         if(!this.href){
26682             this.el.on('click', this.onClick, this);
26683         }
26684     },
26685     
26686     onClick : function(e)
26687     {
26688         Roo.log('img onclick');
26689         this.fireEvent('click', this, e);
26690     }
26691    
26692 });
26693
26694  
26695 /*
26696  * - LGPL
26697  *
26698  * numberBox
26699  * 
26700  */
26701 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26702
26703 /**
26704  * @class Roo.bootstrap.dash.NumberBox
26705  * @extends Roo.bootstrap.Component
26706  * Bootstrap NumberBox class
26707  * @cfg {String} headline Box headline
26708  * @cfg {String} content Box content
26709  * @cfg {String} icon Box icon
26710  * @cfg {String} footer Footer text
26711  * @cfg {String} fhref Footer href
26712  * 
26713  * @constructor
26714  * Create a new NumberBox
26715  * @param {Object} config The config object
26716  */
26717
26718
26719 Roo.bootstrap.dash.NumberBox = function(config){
26720     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26721     
26722 };
26723
26724 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26725     
26726     headline : '',
26727     content : '',
26728     icon : '',
26729     footer : '',
26730     fhref : '',
26731     ficon : '',
26732     
26733     getAutoCreate : function(){
26734         
26735         var cfg = {
26736             tag : 'div',
26737             cls : 'small-box ',
26738             cn : [
26739                 {
26740                     tag : 'div',
26741                     cls : 'inner',
26742                     cn :[
26743                         {
26744                             tag : 'h3',
26745                             cls : 'roo-headline',
26746                             html : this.headline
26747                         },
26748                         {
26749                             tag : 'p',
26750                             cls : 'roo-content',
26751                             html : this.content
26752                         }
26753                     ]
26754                 }
26755             ]
26756         };
26757         
26758         if(this.icon){
26759             cfg.cn.push({
26760                 tag : 'div',
26761                 cls : 'icon',
26762                 cn :[
26763                     {
26764                         tag : 'i',
26765                         cls : 'ion ' + this.icon
26766                     }
26767                 ]
26768             });
26769         }
26770         
26771         if(this.footer){
26772             var footer = {
26773                 tag : 'a',
26774                 cls : 'small-box-footer',
26775                 href : this.fhref || '#',
26776                 html : this.footer
26777             };
26778             
26779             cfg.cn.push(footer);
26780             
26781         }
26782         
26783         return  cfg;
26784     },
26785
26786     onRender : function(ct,position){
26787         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26788
26789
26790        
26791                 
26792     },
26793
26794     setHeadline: function (value)
26795     {
26796         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26797     },
26798     
26799     setFooter: function (value, href)
26800     {
26801         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26802         
26803         if(href){
26804             this.el.select('a.small-box-footer',true).first().attr('href', href);
26805         }
26806         
26807     },
26808
26809     setContent: function (value)
26810     {
26811         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26812     },
26813
26814     initEvents: function() 
26815     {   
26816         
26817     }
26818     
26819 });
26820
26821  
26822 /*
26823  * - LGPL
26824  *
26825  * TabBox
26826  * 
26827  */
26828 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26829
26830 /**
26831  * @class Roo.bootstrap.dash.TabBox
26832  * @extends Roo.bootstrap.Component
26833  * Bootstrap TabBox class
26834  * @cfg {String} title Title of the TabBox
26835  * @cfg {String} icon Icon of the TabBox
26836  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26837  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26838  * 
26839  * @constructor
26840  * Create a new TabBox
26841  * @param {Object} config The config object
26842  */
26843
26844
26845 Roo.bootstrap.dash.TabBox = function(config){
26846     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26847     this.addEvents({
26848         // raw events
26849         /**
26850          * @event addpane
26851          * When a pane is added
26852          * @param {Roo.bootstrap.dash.TabPane} pane
26853          */
26854         "addpane" : true,
26855         /**
26856          * @event activatepane
26857          * When a pane is activated
26858          * @param {Roo.bootstrap.dash.TabPane} pane
26859          */
26860         "activatepane" : true
26861         
26862          
26863     });
26864     
26865     this.panes = [];
26866 };
26867
26868 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26869
26870     title : '',
26871     icon : false,
26872     showtabs : true,
26873     tabScrollable : false,
26874     
26875     getChildContainer : function()
26876     {
26877         return this.el.select('.tab-content', true).first();
26878     },
26879     
26880     getAutoCreate : function(){
26881         
26882         var header = {
26883             tag: 'li',
26884             cls: 'pull-left header',
26885             html: this.title,
26886             cn : []
26887         };
26888         
26889         if(this.icon){
26890             header.cn.push({
26891                 tag: 'i',
26892                 cls: 'fa ' + this.icon
26893             });
26894         }
26895         
26896         var h = {
26897             tag: 'ul',
26898             cls: 'nav nav-tabs pull-right',
26899             cn: [
26900                 header
26901             ]
26902         };
26903         
26904         if(this.tabScrollable){
26905             h = {
26906                 tag: 'div',
26907                 cls: 'tab-header',
26908                 cn: [
26909                     {
26910                         tag: 'ul',
26911                         cls: 'nav nav-tabs pull-right',
26912                         cn: [
26913                             header
26914                         ]
26915                     }
26916                 ]
26917             };
26918         }
26919         
26920         var cfg = {
26921             tag: 'div',
26922             cls: 'nav-tabs-custom',
26923             cn: [
26924                 h,
26925                 {
26926                     tag: 'div',
26927                     cls: 'tab-content no-padding',
26928                     cn: []
26929                 }
26930             ]
26931         };
26932
26933         return  cfg;
26934     },
26935     initEvents : function()
26936     {
26937         //Roo.log('add add pane handler');
26938         this.on('addpane', this.onAddPane, this);
26939     },
26940      /**
26941      * Updates the box title
26942      * @param {String} html to set the title to.
26943      */
26944     setTitle : function(value)
26945     {
26946         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26947     },
26948     onAddPane : function(pane)
26949     {
26950         this.panes.push(pane);
26951         //Roo.log('addpane');
26952         //Roo.log(pane);
26953         // tabs are rendere left to right..
26954         if(!this.showtabs){
26955             return;
26956         }
26957         
26958         var ctr = this.el.select('.nav-tabs', true).first();
26959          
26960          
26961         var existing = ctr.select('.nav-tab',true);
26962         var qty = existing.getCount();;
26963         
26964         
26965         var tab = ctr.createChild({
26966             tag : 'li',
26967             cls : 'nav-tab' + (qty ? '' : ' active'),
26968             cn : [
26969                 {
26970                     tag : 'a',
26971                     href:'#',
26972                     html : pane.title
26973                 }
26974             ]
26975         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26976         pane.tab = tab;
26977         
26978         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26979         if (!qty) {
26980             pane.el.addClass('active');
26981         }
26982         
26983                 
26984     },
26985     onTabClick : function(ev,un,ob,pane)
26986     {
26987         //Roo.log('tab - prev default');
26988         ev.preventDefault();
26989         
26990         
26991         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26992         pane.tab.addClass('active');
26993         //Roo.log(pane.title);
26994         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26995         // technically we should have a deactivate event.. but maybe add later.
26996         // and it should not de-activate the selected tab...
26997         this.fireEvent('activatepane', pane);
26998         pane.el.addClass('active');
26999         pane.fireEvent('activate');
27000         
27001         
27002     },
27003     
27004     getActivePane : function()
27005     {
27006         var r = false;
27007         Roo.each(this.panes, function(p) {
27008             if(p.el.hasClass('active')){
27009                 r = p;
27010                 return false;
27011             }
27012             
27013             return;
27014         });
27015         
27016         return r;
27017     }
27018     
27019     
27020 });
27021
27022  
27023 /*
27024  * - LGPL
27025  *
27026  * Tab pane
27027  * 
27028  */
27029 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27030 /**
27031  * @class Roo.bootstrap.TabPane
27032  * @extends Roo.bootstrap.Component
27033  * Bootstrap TabPane class
27034  * @cfg {Boolean} active (false | true) Default false
27035  * @cfg {String} title title of panel
27036
27037  * 
27038  * @constructor
27039  * Create a new TabPane
27040  * @param {Object} config The config object
27041  */
27042
27043 Roo.bootstrap.dash.TabPane = function(config){
27044     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27045     
27046     this.addEvents({
27047         // raw events
27048         /**
27049          * @event activate
27050          * When a pane is activated
27051          * @param {Roo.bootstrap.dash.TabPane} pane
27052          */
27053         "activate" : true
27054          
27055     });
27056 };
27057
27058 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27059     
27060     active : false,
27061     title : '',
27062     
27063     // the tabBox that this is attached to.
27064     tab : false,
27065      
27066     getAutoCreate : function() 
27067     {
27068         var cfg = {
27069             tag: 'div',
27070             cls: 'tab-pane'
27071         };
27072         
27073         if(this.active){
27074             cfg.cls += ' active';
27075         }
27076         
27077         return cfg;
27078     },
27079     initEvents  : function()
27080     {
27081         //Roo.log('trigger add pane handler');
27082         this.parent().fireEvent('addpane', this)
27083     },
27084     
27085      /**
27086      * Updates the tab title 
27087      * @param {String} html to set the title to.
27088      */
27089     setTitle: function(str)
27090     {
27091         if (!this.tab) {
27092             return;
27093         }
27094         this.title = str;
27095         this.tab.select('a', true).first().dom.innerHTML = str;
27096         
27097     }
27098     
27099     
27100     
27101 });
27102
27103  
27104
27105
27106  /*
27107  * - LGPL
27108  *
27109  * menu
27110  * 
27111  */
27112 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27113
27114 /**
27115  * @class Roo.bootstrap.menu.Menu
27116  * @extends Roo.bootstrap.Component
27117  * Bootstrap Menu class - container for Menu
27118  * @cfg {String} html Text of the menu
27119  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27120  * @cfg {String} icon Font awesome icon
27121  * @cfg {String} pos Menu align to (top | bottom) default bottom
27122  * 
27123  * 
27124  * @constructor
27125  * Create a new Menu
27126  * @param {Object} config The config object
27127  */
27128
27129
27130 Roo.bootstrap.menu.Menu = function(config){
27131     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27132     
27133     this.addEvents({
27134         /**
27135          * @event beforeshow
27136          * Fires before this menu is displayed
27137          * @param {Roo.bootstrap.menu.Menu} this
27138          */
27139         beforeshow : true,
27140         /**
27141          * @event beforehide
27142          * Fires before this menu is hidden
27143          * @param {Roo.bootstrap.menu.Menu} this
27144          */
27145         beforehide : true,
27146         /**
27147          * @event show
27148          * Fires after this menu is displayed
27149          * @param {Roo.bootstrap.menu.Menu} this
27150          */
27151         show : true,
27152         /**
27153          * @event hide
27154          * Fires after this menu is hidden
27155          * @param {Roo.bootstrap.menu.Menu} this
27156          */
27157         hide : true,
27158         /**
27159          * @event click
27160          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27161          * @param {Roo.bootstrap.menu.Menu} this
27162          * @param {Roo.EventObject} e
27163          */
27164         click : true
27165     });
27166     
27167 };
27168
27169 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27170     
27171     submenu : false,
27172     html : '',
27173     weight : 'default',
27174     icon : false,
27175     pos : 'bottom',
27176     
27177     
27178     getChildContainer : function() {
27179         if(this.isSubMenu){
27180             return this.el;
27181         }
27182         
27183         return this.el.select('ul.dropdown-menu', true).first();  
27184     },
27185     
27186     getAutoCreate : function()
27187     {
27188         var text = [
27189             {
27190                 tag : 'span',
27191                 cls : 'roo-menu-text',
27192                 html : this.html
27193             }
27194         ];
27195         
27196         if(this.icon){
27197             text.unshift({
27198                 tag : 'i',
27199                 cls : 'fa ' + this.icon
27200             })
27201         }
27202         
27203         
27204         var cfg = {
27205             tag : 'div',
27206             cls : 'btn-group',
27207             cn : [
27208                 {
27209                     tag : 'button',
27210                     cls : 'dropdown-button btn btn-' + this.weight,
27211                     cn : text
27212                 },
27213                 {
27214                     tag : 'button',
27215                     cls : 'dropdown-toggle btn btn-' + this.weight,
27216                     cn : [
27217                         {
27218                             tag : 'span',
27219                             cls : 'caret'
27220                         }
27221                     ]
27222                 },
27223                 {
27224                     tag : 'ul',
27225                     cls : 'dropdown-menu'
27226                 }
27227             ]
27228             
27229         };
27230         
27231         if(this.pos == 'top'){
27232             cfg.cls += ' dropup';
27233         }
27234         
27235         if(this.isSubMenu){
27236             cfg = {
27237                 tag : 'ul',
27238                 cls : 'dropdown-menu'
27239             }
27240         }
27241         
27242         return cfg;
27243     },
27244     
27245     onRender : function(ct, position)
27246     {
27247         this.isSubMenu = ct.hasClass('dropdown-submenu');
27248         
27249         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27250     },
27251     
27252     initEvents : function() 
27253     {
27254         if(this.isSubMenu){
27255             return;
27256         }
27257         
27258         this.hidden = true;
27259         
27260         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27261         this.triggerEl.on('click', this.onTriggerPress, this);
27262         
27263         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27264         this.buttonEl.on('click', this.onClick, this);
27265         
27266     },
27267     
27268     list : function()
27269     {
27270         if(this.isSubMenu){
27271             return this.el;
27272         }
27273         
27274         return this.el.select('ul.dropdown-menu', true).first();
27275     },
27276     
27277     onClick : function(e)
27278     {
27279         this.fireEvent("click", this, e);
27280     },
27281     
27282     onTriggerPress  : function(e)
27283     {   
27284         if (this.isVisible()) {
27285             this.hide();
27286         } else {
27287             this.show();
27288         }
27289     },
27290     
27291     isVisible : function(){
27292         return !this.hidden;
27293     },
27294     
27295     show : function()
27296     {
27297         this.fireEvent("beforeshow", this);
27298         
27299         this.hidden = false;
27300         this.el.addClass('open');
27301         
27302         Roo.get(document).on("mouseup", this.onMouseUp, this);
27303         
27304         this.fireEvent("show", this);
27305         
27306         
27307     },
27308     
27309     hide : function()
27310     {
27311         this.fireEvent("beforehide", this);
27312         
27313         this.hidden = true;
27314         this.el.removeClass('open');
27315         
27316         Roo.get(document).un("mouseup", this.onMouseUp);
27317         
27318         this.fireEvent("hide", this);
27319     },
27320     
27321     onMouseUp : function()
27322     {
27323         this.hide();
27324     }
27325     
27326 });
27327
27328  
27329  /*
27330  * - LGPL
27331  *
27332  * menu item
27333  * 
27334  */
27335 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27336
27337 /**
27338  * @class Roo.bootstrap.menu.Item
27339  * @extends Roo.bootstrap.Component
27340  * Bootstrap MenuItem class
27341  * @cfg {Boolean} submenu (true | false) default false
27342  * @cfg {String} html text of the item
27343  * @cfg {String} href the link
27344  * @cfg {Boolean} disable (true | false) default false
27345  * @cfg {Boolean} preventDefault (true | false) default true
27346  * @cfg {String} icon Font awesome icon
27347  * @cfg {String} pos Submenu align to (left | right) default right 
27348  * 
27349  * 
27350  * @constructor
27351  * Create a new Item
27352  * @param {Object} config The config object
27353  */
27354
27355
27356 Roo.bootstrap.menu.Item = function(config){
27357     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27358     this.addEvents({
27359         /**
27360          * @event mouseover
27361          * Fires when the mouse is hovering over this menu
27362          * @param {Roo.bootstrap.menu.Item} this
27363          * @param {Roo.EventObject} e
27364          */
27365         mouseover : true,
27366         /**
27367          * @event mouseout
27368          * Fires when the mouse exits this menu
27369          * @param {Roo.bootstrap.menu.Item} this
27370          * @param {Roo.EventObject} e
27371          */
27372         mouseout : true,
27373         // raw events
27374         /**
27375          * @event click
27376          * The raw click event for the entire grid.
27377          * @param {Roo.EventObject} e
27378          */
27379         click : true
27380     });
27381 };
27382
27383 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27384     
27385     submenu : false,
27386     href : '',
27387     html : '',
27388     preventDefault: true,
27389     disable : false,
27390     icon : false,
27391     pos : 'right',
27392     
27393     getAutoCreate : function()
27394     {
27395         var text = [
27396             {
27397                 tag : 'span',
27398                 cls : 'roo-menu-item-text',
27399                 html : this.html
27400             }
27401         ];
27402         
27403         if(this.icon){
27404             text.unshift({
27405                 tag : 'i',
27406                 cls : 'fa ' + this.icon
27407             })
27408         }
27409         
27410         var cfg = {
27411             tag : 'li',
27412             cn : [
27413                 {
27414                     tag : 'a',
27415                     href : this.href || '#',
27416                     cn : text
27417                 }
27418             ]
27419         };
27420         
27421         if(this.disable){
27422             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27423         }
27424         
27425         if(this.submenu){
27426             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27427             
27428             if(this.pos == 'left'){
27429                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27430             }
27431         }
27432         
27433         return cfg;
27434     },
27435     
27436     initEvents : function() 
27437     {
27438         this.el.on('mouseover', this.onMouseOver, this);
27439         this.el.on('mouseout', this.onMouseOut, this);
27440         
27441         this.el.select('a', true).first().on('click', this.onClick, this);
27442         
27443     },
27444     
27445     onClick : function(e)
27446     {
27447         if(this.preventDefault){
27448             e.preventDefault();
27449         }
27450         
27451         this.fireEvent("click", this, e);
27452     },
27453     
27454     onMouseOver : function(e)
27455     {
27456         if(this.submenu && this.pos == 'left'){
27457             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27458         }
27459         
27460         this.fireEvent("mouseover", this, e);
27461     },
27462     
27463     onMouseOut : function(e)
27464     {
27465         this.fireEvent("mouseout", this, e);
27466     }
27467 });
27468
27469  
27470
27471  /*
27472  * - LGPL
27473  *
27474  * menu separator
27475  * 
27476  */
27477 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27478
27479 /**
27480  * @class Roo.bootstrap.menu.Separator
27481  * @extends Roo.bootstrap.Component
27482  * Bootstrap Separator class
27483  * 
27484  * @constructor
27485  * Create a new Separator
27486  * @param {Object} config The config object
27487  */
27488
27489
27490 Roo.bootstrap.menu.Separator = function(config){
27491     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27492 };
27493
27494 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27495     
27496     getAutoCreate : function(){
27497         var cfg = {
27498             tag : 'li',
27499             cls: 'divider'
27500         };
27501         
27502         return cfg;
27503     }
27504    
27505 });
27506
27507  
27508
27509  /*
27510  * - LGPL
27511  *
27512  * Tooltip
27513  * 
27514  */
27515
27516 /**
27517  * @class Roo.bootstrap.Tooltip
27518  * Bootstrap Tooltip class
27519  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27520  * to determine which dom element triggers the tooltip.
27521  * 
27522  * It needs to add support for additional attributes like tooltip-position
27523  * 
27524  * @constructor
27525  * Create a new Toolti
27526  * @param {Object} config The config object
27527  */
27528
27529 Roo.bootstrap.Tooltip = function(config){
27530     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27531     
27532     this.alignment = Roo.bootstrap.Tooltip.alignment;
27533     
27534     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27535         this.alignment = config.alignment;
27536     }
27537     
27538 };
27539
27540 Roo.apply(Roo.bootstrap.Tooltip, {
27541     /**
27542      * @function init initialize tooltip monitoring.
27543      * @static
27544      */
27545     currentEl : false,
27546     currentTip : false,
27547     currentRegion : false,
27548     
27549     //  init : delay?
27550     
27551     init : function()
27552     {
27553         Roo.get(document).on('mouseover', this.enter ,this);
27554         Roo.get(document).on('mouseout', this.leave, this);
27555          
27556         
27557         this.currentTip = new Roo.bootstrap.Tooltip();
27558     },
27559     
27560     enter : function(ev)
27561     {
27562         var dom = ev.getTarget();
27563         
27564         //Roo.log(['enter',dom]);
27565         var el = Roo.fly(dom);
27566         if (this.currentEl) {
27567             //Roo.log(dom);
27568             //Roo.log(this.currentEl);
27569             //Roo.log(this.currentEl.contains(dom));
27570             if (this.currentEl == el) {
27571                 return;
27572             }
27573             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27574                 return;
27575             }
27576
27577         }
27578         
27579         if (this.currentTip.el) {
27580             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27581         }    
27582         //Roo.log(ev);
27583         
27584         if(!el || el.dom == document){
27585             return;
27586         }
27587         
27588         var bindEl = el;
27589         
27590         // you can not look for children, as if el is the body.. then everythign is the child..
27591         if (!el.attr('tooltip')) { //
27592             if (!el.select("[tooltip]").elements.length) {
27593                 return;
27594             }
27595             // is the mouse over this child...?
27596             bindEl = el.select("[tooltip]").first();
27597             var xy = ev.getXY();
27598             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27599                 //Roo.log("not in region.");
27600                 return;
27601             }
27602             //Roo.log("child element over..");
27603             
27604         }
27605         this.currentEl = bindEl;
27606         this.currentTip.bind(bindEl);
27607         this.currentRegion = Roo.lib.Region.getRegion(dom);
27608         this.currentTip.enter();
27609         
27610     },
27611     leave : function(ev)
27612     {
27613         var dom = ev.getTarget();
27614         //Roo.log(['leave',dom]);
27615         if (!this.currentEl) {
27616             return;
27617         }
27618         
27619         
27620         if (dom != this.currentEl.dom) {
27621             return;
27622         }
27623         var xy = ev.getXY();
27624         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27625             return;
27626         }
27627         // only activate leave if mouse cursor is outside... bounding box..
27628         
27629         
27630         
27631         
27632         if (this.currentTip) {
27633             this.currentTip.leave();
27634         }
27635         //Roo.log('clear currentEl');
27636         this.currentEl = false;
27637         
27638         
27639     },
27640     alignment : {
27641         'left' : ['r-l', [-2,0], 'right'],
27642         'right' : ['l-r', [2,0], 'left'],
27643         'bottom' : ['t-b', [0,2], 'top'],
27644         'top' : [ 'b-t', [0,-2], 'bottom']
27645     }
27646     
27647 });
27648
27649
27650 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27651     
27652     
27653     bindEl : false,
27654     
27655     delay : null, // can be { show : 300 , hide: 500}
27656     
27657     timeout : null,
27658     
27659     hoverState : null, //???
27660     
27661     placement : 'bottom', 
27662     
27663     alignment : false,
27664     
27665     getAutoCreate : function(){
27666     
27667         var cfg = {
27668            cls : 'tooltip',
27669            role : 'tooltip',
27670            cn : [
27671                 {
27672                     cls : 'tooltip-arrow'
27673                 },
27674                 {
27675                     cls : 'tooltip-inner'
27676                 }
27677            ]
27678         };
27679         
27680         return cfg;
27681     },
27682     bind : function(el)
27683     {
27684         this.bindEl = el;
27685     },
27686       
27687     
27688     enter : function () {
27689        
27690         if (this.timeout != null) {
27691             clearTimeout(this.timeout);
27692         }
27693         
27694         this.hoverState = 'in';
27695          //Roo.log("enter - show");
27696         if (!this.delay || !this.delay.show) {
27697             this.show();
27698             return;
27699         }
27700         var _t = this;
27701         this.timeout = setTimeout(function () {
27702             if (_t.hoverState == 'in') {
27703                 _t.show();
27704             }
27705         }, this.delay.show);
27706     },
27707     leave : function()
27708     {
27709         clearTimeout(this.timeout);
27710     
27711         this.hoverState = 'out';
27712          if (!this.delay || !this.delay.hide) {
27713             this.hide();
27714             return;
27715         }
27716        
27717         var _t = this;
27718         this.timeout = setTimeout(function () {
27719             //Roo.log("leave - timeout");
27720             
27721             if (_t.hoverState == 'out') {
27722                 _t.hide();
27723                 Roo.bootstrap.Tooltip.currentEl = false;
27724             }
27725         }, delay);
27726     },
27727     
27728     show : function (msg)
27729     {
27730         if (!this.el) {
27731             this.render(document.body);
27732         }
27733         // set content.
27734         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27735         
27736         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27737         
27738         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27739         
27740         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27741         
27742         var placement = typeof this.placement == 'function' ?
27743             this.placement.call(this, this.el, on_el) :
27744             this.placement;
27745             
27746         var autoToken = /\s?auto?\s?/i;
27747         var autoPlace = autoToken.test(placement);
27748         if (autoPlace) {
27749             placement = placement.replace(autoToken, '') || 'top';
27750         }
27751         
27752         //this.el.detach()
27753         //this.el.setXY([0,0]);
27754         this.el.show();
27755         //this.el.dom.style.display='block';
27756         
27757         //this.el.appendTo(on_el);
27758         
27759         var p = this.getPosition();
27760         var box = this.el.getBox();
27761         
27762         if (autoPlace) {
27763             // fixme..
27764         }
27765         
27766         var align = this.alignment[placement];
27767         
27768         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27769         
27770         if(placement == 'top' || placement == 'bottom'){
27771             if(xy[0] < 0){
27772                 placement = 'right';
27773             }
27774             
27775             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27776                 placement = 'left';
27777             }
27778             
27779             var scroll = Roo.select('body', true).first().getScroll();
27780             
27781             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27782                 placement = 'top';
27783             }
27784             
27785             align = this.alignment[placement];
27786         }
27787         
27788         this.el.alignTo(this.bindEl, align[0],align[1]);
27789         //var arrow = this.el.select('.arrow',true).first();
27790         //arrow.set(align[2], 
27791         
27792         this.el.addClass(placement);
27793         
27794         this.el.addClass('in fade');
27795         
27796         this.hoverState = null;
27797         
27798         if (this.el.hasClass('fade')) {
27799             // fade it?
27800         }
27801         
27802     },
27803     hide : function()
27804     {
27805          
27806         if (!this.el) {
27807             return;
27808         }
27809         //this.el.setXY([0,0]);
27810         this.el.removeClass('in');
27811         //this.el.hide();
27812         
27813     }
27814     
27815 });
27816  
27817
27818  /*
27819  * - LGPL
27820  *
27821  * Location Picker
27822  * 
27823  */
27824
27825 /**
27826  * @class Roo.bootstrap.LocationPicker
27827  * @extends Roo.bootstrap.Component
27828  * Bootstrap LocationPicker class
27829  * @cfg {Number} latitude Position when init default 0
27830  * @cfg {Number} longitude Position when init default 0
27831  * @cfg {Number} zoom default 15
27832  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27833  * @cfg {Boolean} mapTypeControl default false
27834  * @cfg {Boolean} disableDoubleClickZoom default false
27835  * @cfg {Boolean} scrollwheel default true
27836  * @cfg {Boolean} streetViewControl default false
27837  * @cfg {Number} radius default 0
27838  * @cfg {String} locationName
27839  * @cfg {Boolean} draggable default true
27840  * @cfg {Boolean} enableAutocomplete default false
27841  * @cfg {Boolean} enableReverseGeocode default true
27842  * @cfg {String} markerTitle
27843  * 
27844  * @constructor
27845  * Create a new LocationPicker
27846  * @param {Object} config The config object
27847  */
27848
27849
27850 Roo.bootstrap.LocationPicker = function(config){
27851     
27852     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27853     
27854     this.addEvents({
27855         /**
27856          * @event initial
27857          * Fires when the picker initialized.
27858          * @param {Roo.bootstrap.LocationPicker} this
27859          * @param {Google Location} location
27860          */
27861         initial : true,
27862         /**
27863          * @event positionchanged
27864          * Fires when the picker position changed.
27865          * @param {Roo.bootstrap.LocationPicker} this
27866          * @param {Google Location} location
27867          */
27868         positionchanged : true,
27869         /**
27870          * @event resize
27871          * Fires when the map resize.
27872          * @param {Roo.bootstrap.LocationPicker} this
27873          */
27874         resize : true,
27875         /**
27876          * @event show
27877          * Fires when the map show.
27878          * @param {Roo.bootstrap.LocationPicker} this
27879          */
27880         show : true,
27881         /**
27882          * @event hide
27883          * Fires when the map hide.
27884          * @param {Roo.bootstrap.LocationPicker} this
27885          */
27886         hide : true,
27887         /**
27888          * @event mapClick
27889          * Fires when click the map.
27890          * @param {Roo.bootstrap.LocationPicker} this
27891          * @param {Map event} e
27892          */
27893         mapClick : true,
27894         /**
27895          * @event mapRightClick
27896          * Fires when right click the map.
27897          * @param {Roo.bootstrap.LocationPicker} this
27898          * @param {Map event} e
27899          */
27900         mapRightClick : true,
27901         /**
27902          * @event markerClick
27903          * Fires when click the marker.
27904          * @param {Roo.bootstrap.LocationPicker} this
27905          * @param {Map event} e
27906          */
27907         markerClick : true,
27908         /**
27909          * @event markerRightClick
27910          * Fires when right click the marker.
27911          * @param {Roo.bootstrap.LocationPicker} this
27912          * @param {Map event} e
27913          */
27914         markerRightClick : true,
27915         /**
27916          * @event OverlayViewDraw
27917          * Fires when OverlayView Draw
27918          * @param {Roo.bootstrap.LocationPicker} this
27919          */
27920         OverlayViewDraw : true,
27921         /**
27922          * @event OverlayViewOnAdd
27923          * Fires when OverlayView Draw
27924          * @param {Roo.bootstrap.LocationPicker} this
27925          */
27926         OverlayViewOnAdd : true,
27927         /**
27928          * @event OverlayViewOnRemove
27929          * Fires when OverlayView Draw
27930          * @param {Roo.bootstrap.LocationPicker} this
27931          */
27932         OverlayViewOnRemove : true,
27933         /**
27934          * @event OverlayViewShow
27935          * Fires when OverlayView Draw
27936          * @param {Roo.bootstrap.LocationPicker} this
27937          * @param {Pixel} cpx
27938          */
27939         OverlayViewShow : true,
27940         /**
27941          * @event OverlayViewHide
27942          * Fires when OverlayView Draw
27943          * @param {Roo.bootstrap.LocationPicker} this
27944          */
27945         OverlayViewHide : true,
27946         /**
27947          * @event loadexception
27948          * Fires when load google lib failed.
27949          * @param {Roo.bootstrap.LocationPicker} this
27950          */
27951         loadexception : true
27952     });
27953         
27954 };
27955
27956 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27957     
27958     gMapContext: false,
27959     
27960     latitude: 0,
27961     longitude: 0,
27962     zoom: 15,
27963     mapTypeId: false,
27964     mapTypeControl: false,
27965     disableDoubleClickZoom: false,
27966     scrollwheel: true,
27967     streetViewControl: false,
27968     radius: 0,
27969     locationName: '',
27970     draggable: true,
27971     enableAutocomplete: false,
27972     enableReverseGeocode: true,
27973     markerTitle: '',
27974     
27975     getAutoCreate: function()
27976     {
27977
27978         var cfg = {
27979             tag: 'div',
27980             cls: 'roo-location-picker'
27981         };
27982         
27983         return cfg
27984     },
27985     
27986     initEvents: function(ct, position)
27987     {       
27988         if(!this.el.getWidth() || this.isApplied()){
27989             return;
27990         }
27991         
27992         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27993         
27994         this.initial();
27995     },
27996     
27997     initial: function()
27998     {
27999         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28000             this.fireEvent('loadexception', this);
28001             return;
28002         }
28003         
28004         if(!this.mapTypeId){
28005             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28006         }
28007         
28008         this.gMapContext = this.GMapContext();
28009         
28010         this.initOverlayView();
28011         
28012         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28013         
28014         var _this = this;
28015                 
28016         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28017             _this.setPosition(_this.gMapContext.marker.position);
28018         });
28019         
28020         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28021             _this.fireEvent('mapClick', this, event);
28022             
28023         });
28024
28025         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28026             _this.fireEvent('mapRightClick', this, event);
28027             
28028         });
28029         
28030         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28031             _this.fireEvent('markerClick', this, event);
28032             
28033         });
28034
28035         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28036             _this.fireEvent('markerRightClick', this, event);
28037             
28038         });
28039         
28040         this.setPosition(this.gMapContext.location);
28041         
28042         this.fireEvent('initial', this, this.gMapContext.location);
28043     },
28044     
28045     initOverlayView: function()
28046     {
28047         var _this = this;
28048         
28049         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28050             
28051             draw: function()
28052             {
28053                 _this.fireEvent('OverlayViewDraw', _this);
28054             },
28055             
28056             onAdd: function()
28057             {
28058                 _this.fireEvent('OverlayViewOnAdd', _this);
28059             },
28060             
28061             onRemove: function()
28062             {
28063                 _this.fireEvent('OverlayViewOnRemove', _this);
28064             },
28065             
28066             show: function(cpx)
28067             {
28068                 _this.fireEvent('OverlayViewShow', _this, cpx);
28069             },
28070             
28071             hide: function()
28072             {
28073                 _this.fireEvent('OverlayViewHide', _this);
28074             }
28075             
28076         });
28077     },
28078     
28079     fromLatLngToContainerPixel: function(event)
28080     {
28081         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28082     },
28083     
28084     isApplied: function() 
28085     {
28086         return this.getGmapContext() == false ? false : true;
28087     },
28088     
28089     getGmapContext: function() 
28090     {
28091         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28092     },
28093     
28094     GMapContext: function() 
28095     {
28096         var position = new google.maps.LatLng(this.latitude, this.longitude);
28097         
28098         var _map = new google.maps.Map(this.el.dom, {
28099             center: position,
28100             zoom: this.zoom,
28101             mapTypeId: this.mapTypeId,
28102             mapTypeControl: this.mapTypeControl,
28103             disableDoubleClickZoom: this.disableDoubleClickZoom,
28104             scrollwheel: this.scrollwheel,
28105             streetViewControl: this.streetViewControl,
28106             locationName: this.locationName,
28107             draggable: this.draggable,
28108             enableAutocomplete: this.enableAutocomplete,
28109             enableReverseGeocode: this.enableReverseGeocode
28110         });
28111         
28112         var _marker = new google.maps.Marker({
28113             position: position,
28114             map: _map,
28115             title: this.markerTitle,
28116             draggable: this.draggable
28117         });
28118         
28119         return {
28120             map: _map,
28121             marker: _marker,
28122             circle: null,
28123             location: position,
28124             radius: this.radius,
28125             locationName: this.locationName,
28126             addressComponents: {
28127                 formatted_address: null,
28128                 addressLine1: null,
28129                 addressLine2: null,
28130                 streetName: null,
28131                 streetNumber: null,
28132                 city: null,
28133                 district: null,
28134                 state: null,
28135                 stateOrProvince: null
28136             },
28137             settings: this,
28138             domContainer: this.el.dom,
28139             geodecoder: new google.maps.Geocoder()
28140         };
28141     },
28142     
28143     drawCircle: function(center, radius, options) 
28144     {
28145         if (this.gMapContext.circle != null) {
28146             this.gMapContext.circle.setMap(null);
28147         }
28148         if (radius > 0) {
28149             radius *= 1;
28150             options = Roo.apply({}, options, {
28151                 strokeColor: "#0000FF",
28152                 strokeOpacity: .35,
28153                 strokeWeight: 2,
28154                 fillColor: "#0000FF",
28155                 fillOpacity: .2
28156             });
28157             
28158             options.map = this.gMapContext.map;
28159             options.radius = radius;
28160             options.center = center;
28161             this.gMapContext.circle = new google.maps.Circle(options);
28162             return this.gMapContext.circle;
28163         }
28164         
28165         return null;
28166     },
28167     
28168     setPosition: function(location) 
28169     {
28170         this.gMapContext.location = location;
28171         this.gMapContext.marker.setPosition(location);
28172         this.gMapContext.map.panTo(location);
28173         this.drawCircle(location, this.gMapContext.radius, {});
28174         
28175         var _this = this;
28176         
28177         if (this.gMapContext.settings.enableReverseGeocode) {
28178             this.gMapContext.geodecoder.geocode({
28179                 latLng: this.gMapContext.location
28180             }, function(results, status) {
28181                 
28182                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28183                     _this.gMapContext.locationName = results[0].formatted_address;
28184                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28185                     
28186                     _this.fireEvent('positionchanged', this, location);
28187                 }
28188             });
28189             
28190             return;
28191         }
28192         
28193         this.fireEvent('positionchanged', this, location);
28194     },
28195     
28196     resize: function()
28197     {
28198         google.maps.event.trigger(this.gMapContext.map, "resize");
28199         
28200         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28201         
28202         this.fireEvent('resize', this);
28203     },
28204     
28205     setPositionByLatLng: function(latitude, longitude)
28206     {
28207         this.setPosition(new google.maps.LatLng(latitude, longitude));
28208     },
28209     
28210     getCurrentPosition: function() 
28211     {
28212         return {
28213             latitude: this.gMapContext.location.lat(),
28214             longitude: this.gMapContext.location.lng()
28215         };
28216     },
28217     
28218     getAddressName: function() 
28219     {
28220         return this.gMapContext.locationName;
28221     },
28222     
28223     getAddressComponents: function() 
28224     {
28225         return this.gMapContext.addressComponents;
28226     },
28227     
28228     address_component_from_google_geocode: function(address_components) 
28229     {
28230         var result = {};
28231         
28232         for (var i = 0; i < address_components.length; i++) {
28233             var component = address_components[i];
28234             if (component.types.indexOf("postal_code") >= 0) {
28235                 result.postalCode = component.short_name;
28236             } else if (component.types.indexOf("street_number") >= 0) {
28237                 result.streetNumber = component.short_name;
28238             } else if (component.types.indexOf("route") >= 0) {
28239                 result.streetName = component.short_name;
28240             } else if (component.types.indexOf("neighborhood") >= 0) {
28241                 result.city = component.short_name;
28242             } else if (component.types.indexOf("locality") >= 0) {
28243                 result.city = component.short_name;
28244             } else if (component.types.indexOf("sublocality") >= 0) {
28245                 result.district = component.short_name;
28246             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28247                 result.stateOrProvince = component.short_name;
28248             } else if (component.types.indexOf("country") >= 0) {
28249                 result.country = component.short_name;
28250             }
28251         }
28252         
28253         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28254         result.addressLine2 = "";
28255         return result;
28256     },
28257     
28258     setZoomLevel: function(zoom)
28259     {
28260         this.gMapContext.map.setZoom(zoom);
28261     },
28262     
28263     show: function()
28264     {
28265         if(!this.el){
28266             return;
28267         }
28268         
28269         this.el.show();
28270         
28271         this.resize();
28272         
28273         this.fireEvent('show', this);
28274     },
28275     
28276     hide: function()
28277     {
28278         if(!this.el){
28279             return;
28280         }
28281         
28282         this.el.hide();
28283         
28284         this.fireEvent('hide', this);
28285     }
28286     
28287 });
28288
28289 Roo.apply(Roo.bootstrap.LocationPicker, {
28290     
28291     OverlayView : function(map, options)
28292     {
28293         options = options || {};
28294         
28295         this.setMap(map);
28296     }
28297     
28298     
28299 });/**
28300  * @class Roo.bootstrap.Alert
28301  * @extends Roo.bootstrap.Component
28302  * Bootstrap Alert class - shows an alert area box
28303  * eg
28304  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28305   Enter a valid email address
28306 </div>
28307  * @licence LGPL
28308  * @cfg {String} title The title of alert
28309  * @cfg {String} html The content of alert
28310  * @cfg {String} weight (  success | info | warning | danger )
28311  * @cfg {String} faicon font-awesomeicon
28312  * 
28313  * @constructor
28314  * Create a new alert
28315  * @param {Object} config The config object
28316  */
28317
28318
28319 Roo.bootstrap.Alert = function(config){
28320     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28321     
28322 };
28323
28324 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28325     
28326     title: '',
28327     html: '',
28328     weight: false,
28329     faicon: false,
28330     
28331     getAutoCreate : function()
28332     {
28333         
28334         var cfg = {
28335             tag : 'div',
28336             cls : 'alert',
28337             cn : [
28338                 {
28339                     tag : 'i',
28340                     cls : 'roo-alert-icon'
28341                     
28342                 },
28343                 {
28344                     tag : 'b',
28345                     cls : 'roo-alert-title',
28346                     html : this.title
28347                 },
28348                 {
28349                     tag : 'span',
28350                     cls : 'roo-alert-text',
28351                     html : this.html
28352                 }
28353             ]
28354         };
28355         
28356         if(this.faicon){
28357             cfg.cn[0].cls += ' fa ' + this.faicon;
28358         }
28359         
28360         if(this.weight){
28361             cfg.cls += ' alert-' + this.weight;
28362         }
28363         
28364         return cfg;
28365     },
28366     
28367     initEvents: function() 
28368     {
28369         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28370     },
28371     
28372     setTitle : function(str)
28373     {
28374         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28375     },
28376     
28377     setText : function(str)
28378     {
28379         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28380     },
28381     
28382     setWeight : function(weight)
28383     {
28384         if(this.weight){
28385             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28386         }
28387         
28388         this.weight = weight;
28389         
28390         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28391     },
28392     
28393     setIcon : function(icon)
28394     {
28395         if(this.faicon){
28396             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28397         }
28398         
28399         this.faicon = icon;
28400         
28401         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28402     },
28403     
28404     hide: function() 
28405     {
28406         this.el.hide();   
28407     },
28408     
28409     show: function() 
28410     {  
28411         this.el.show();   
28412     }
28413     
28414 });
28415
28416  
28417 /*
28418 * Licence: LGPL
28419 */
28420
28421 /**
28422  * @class Roo.bootstrap.UploadCropbox
28423  * @extends Roo.bootstrap.Component
28424  * Bootstrap UploadCropbox class
28425  * @cfg {String} emptyText show when image has been loaded
28426  * @cfg {String} rotateNotify show when image too small to rotate
28427  * @cfg {Number} errorTimeout default 3000
28428  * @cfg {Number} minWidth default 300
28429  * @cfg {Number} minHeight default 300
28430  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28431  * @cfg {Boolean} isDocument (true|false) default false
28432  * @cfg {String} url action url
28433  * @cfg {String} paramName default 'imageUpload'
28434  * @cfg {String} method default POST
28435  * @cfg {Boolean} loadMask (true|false) default true
28436  * @cfg {Boolean} loadingText default 'Loading...'
28437  * 
28438  * @constructor
28439  * Create a new UploadCropbox
28440  * @param {Object} config The config object
28441  */
28442
28443 Roo.bootstrap.UploadCropbox = function(config){
28444     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28445     
28446     this.addEvents({
28447         /**
28448          * @event beforeselectfile
28449          * Fire before select file
28450          * @param {Roo.bootstrap.UploadCropbox} this
28451          */
28452         "beforeselectfile" : true,
28453         /**
28454          * @event initial
28455          * Fire after initEvent
28456          * @param {Roo.bootstrap.UploadCropbox} this
28457          */
28458         "initial" : true,
28459         /**
28460          * @event crop
28461          * Fire after initEvent
28462          * @param {Roo.bootstrap.UploadCropbox} this
28463          * @param {String} data
28464          */
28465         "crop" : true,
28466         /**
28467          * @event prepare
28468          * Fire when preparing the file data
28469          * @param {Roo.bootstrap.UploadCropbox} this
28470          * @param {Object} file
28471          */
28472         "prepare" : true,
28473         /**
28474          * @event exception
28475          * Fire when get exception
28476          * @param {Roo.bootstrap.UploadCropbox} this
28477          * @param {XMLHttpRequest} xhr
28478          */
28479         "exception" : true,
28480         /**
28481          * @event beforeloadcanvas
28482          * Fire before load the canvas
28483          * @param {Roo.bootstrap.UploadCropbox} this
28484          * @param {String} src
28485          */
28486         "beforeloadcanvas" : true,
28487         /**
28488          * @event trash
28489          * Fire when trash image
28490          * @param {Roo.bootstrap.UploadCropbox} this
28491          */
28492         "trash" : true,
28493         /**
28494          * @event download
28495          * Fire when download the image
28496          * @param {Roo.bootstrap.UploadCropbox} this
28497          */
28498         "download" : true,
28499         /**
28500          * @event footerbuttonclick
28501          * Fire when footerbuttonclick
28502          * @param {Roo.bootstrap.UploadCropbox} this
28503          * @param {String} type
28504          */
28505         "footerbuttonclick" : true,
28506         /**
28507          * @event resize
28508          * Fire when resize
28509          * @param {Roo.bootstrap.UploadCropbox} this
28510          */
28511         "resize" : true,
28512         /**
28513          * @event rotate
28514          * Fire when rotate the image
28515          * @param {Roo.bootstrap.UploadCropbox} this
28516          * @param {String} pos
28517          */
28518         "rotate" : true,
28519         /**
28520          * @event inspect
28521          * Fire when inspect the file
28522          * @param {Roo.bootstrap.UploadCropbox} this
28523          * @param {Object} file
28524          */
28525         "inspect" : true,
28526         /**
28527          * @event upload
28528          * Fire when xhr upload the file
28529          * @param {Roo.bootstrap.UploadCropbox} this
28530          * @param {Object} data
28531          */
28532         "upload" : true,
28533         /**
28534          * @event arrange
28535          * Fire when arrange the file data
28536          * @param {Roo.bootstrap.UploadCropbox} this
28537          * @param {Object} formData
28538          */
28539         "arrange" : true
28540     });
28541     
28542     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28543 };
28544
28545 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28546     
28547     emptyText : 'Click to upload image',
28548     rotateNotify : 'Image is too small to rotate',
28549     errorTimeout : 3000,
28550     scale : 0,
28551     baseScale : 1,
28552     rotate : 0,
28553     dragable : false,
28554     pinching : false,
28555     mouseX : 0,
28556     mouseY : 0,
28557     cropData : false,
28558     minWidth : 300,
28559     minHeight : 300,
28560     file : false,
28561     exif : {},
28562     baseRotate : 1,
28563     cropType : 'image/jpeg',
28564     buttons : false,
28565     canvasLoaded : false,
28566     isDocument : false,
28567     method : 'POST',
28568     paramName : 'imageUpload',
28569     loadMask : true,
28570     loadingText : 'Loading...',
28571     maskEl : false,
28572     
28573     getAutoCreate : function()
28574     {
28575         var cfg = {
28576             tag : 'div',
28577             cls : 'roo-upload-cropbox',
28578             cn : [
28579                 {
28580                     tag : 'input',
28581                     cls : 'roo-upload-cropbox-selector',
28582                     type : 'file'
28583                 },
28584                 {
28585                     tag : 'div',
28586                     cls : 'roo-upload-cropbox-body',
28587                     style : 'cursor:pointer',
28588                     cn : [
28589                         {
28590                             tag : 'div',
28591                             cls : 'roo-upload-cropbox-preview'
28592                         },
28593                         {
28594                             tag : 'div',
28595                             cls : 'roo-upload-cropbox-thumb'
28596                         },
28597                         {
28598                             tag : 'div',
28599                             cls : 'roo-upload-cropbox-empty-notify',
28600                             html : this.emptyText
28601                         },
28602                         {
28603                             tag : 'div',
28604                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28605                             html : this.rotateNotify
28606                         }
28607                     ]
28608                 },
28609                 {
28610                     tag : 'div',
28611                     cls : 'roo-upload-cropbox-footer',
28612                     cn : {
28613                         tag : 'div',
28614                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28615                         cn : []
28616                     }
28617                 }
28618             ]
28619         };
28620         
28621         return cfg;
28622     },
28623     
28624     onRender : function(ct, position)
28625     {
28626         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28627         
28628         if (this.buttons.length) {
28629             
28630             Roo.each(this.buttons, function(bb) {
28631                 
28632                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28633                 
28634                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28635                 
28636             }, this);
28637         }
28638         
28639         if(this.loadMask){
28640             this.maskEl = this.el;
28641         }
28642     },
28643     
28644     initEvents : function()
28645     {
28646         this.urlAPI = (window.createObjectURL && window) || 
28647                                 (window.URL && URL.revokeObjectURL && URL) || 
28648                                 (window.webkitURL && webkitURL);
28649                         
28650         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28651         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28652         
28653         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28654         this.selectorEl.hide();
28655         
28656         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28657         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28658         
28659         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28660         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28661         this.thumbEl.hide();
28662         
28663         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28664         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28665         
28666         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28667         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28668         this.errorEl.hide();
28669         
28670         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28671         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28672         this.footerEl.hide();
28673         
28674         this.setThumbBoxSize();
28675         
28676         this.bind();
28677         
28678         this.resize();
28679         
28680         this.fireEvent('initial', this);
28681     },
28682
28683     bind : function()
28684     {
28685         var _this = this;
28686         
28687         window.addEventListener("resize", function() { _this.resize(); } );
28688         
28689         this.bodyEl.on('click', this.beforeSelectFile, this);
28690         
28691         if(Roo.isTouch){
28692             this.bodyEl.on('touchstart', this.onTouchStart, this);
28693             this.bodyEl.on('touchmove', this.onTouchMove, this);
28694             this.bodyEl.on('touchend', this.onTouchEnd, this);
28695         }
28696         
28697         if(!Roo.isTouch){
28698             this.bodyEl.on('mousedown', this.onMouseDown, this);
28699             this.bodyEl.on('mousemove', this.onMouseMove, this);
28700             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28701             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28702             Roo.get(document).on('mouseup', this.onMouseUp, this);
28703         }
28704         
28705         this.selectorEl.on('change', this.onFileSelected, this);
28706     },
28707     
28708     reset : function()
28709     {    
28710         this.scale = 0;
28711         this.baseScale = 1;
28712         this.rotate = 0;
28713         this.baseRotate = 1;
28714         this.dragable = false;
28715         this.pinching = false;
28716         this.mouseX = 0;
28717         this.mouseY = 0;
28718         this.cropData = false;
28719         this.notifyEl.dom.innerHTML = this.emptyText;
28720         
28721         this.selectorEl.dom.value = '';
28722         
28723     },
28724     
28725     resize : function()
28726     {
28727         if(this.fireEvent('resize', this) != false){
28728             this.setThumbBoxPosition();
28729             this.setCanvasPosition();
28730         }
28731     },
28732     
28733     onFooterButtonClick : function(e, el, o, type)
28734     {
28735         switch (type) {
28736             case 'rotate-left' :
28737                 this.onRotateLeft(e);
28738                 break;
28739             case 'rotate-right' :
28740                 this.onRotateRight(e);
28741                 break;
28742             case 'picture' :
28743                 this.beforeSelectFile(e);
28744                 break;
28745             case 'trash' :
28746                 this.trash(e);
28747                 break;
28748             case 'crop' :
28749                 this.crop(e);
28750                 break;
28751             case 'download' :
28752                 this.download(e);
28753                 break;
28754             default :
28755                 break;
28756         }
28757         
28758         this.fireEvent('footerbuttonclick', this, type);
28759     },
28760     
28761     beforeSelectFile : function(e)
28762     {
28763         e.preventDefault();
28764         
28765         if(this.fireEvent('beforeselectfile', this) != false){
28766             this.selectorEl.dom.click();
28767         }
28768     },
28769     
28770     onFileSelected : function(e)
28771     {
28772         e.preventDefault();
28773         
28774         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28775             return;
28776         }
28777         
28778         var file = this.selectorEl.dom.files[0];
28779         
28780         if(this.fireEvent('inspect', this, file) != false){
28781             this.prepare(file);
28782         }
28783         
28784     },
28785     
28786     trash : function(e)
28787     {
28788         this.fireEvent('trash', this);
28789     },
28790     
28791     download : function(e)
28792     {
28793         this.fireEvent('download', this);
28794     },
28795     
28796     loadCanvas : function(src)
28797     {   
28798         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28799             
28800             this.reset();
28801             
28802             this.imageEl = document.createElement('img');
28803             
28804             var _this = this;
28805             
28806             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28807             
28808             this.imageEl.src = src;
28809         }
28810     },
28811     
28812     onLoadCanvas : function()
28813     {   
28814         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28815         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28816         
28817         this.bodyEl.un('click', this.beforeSelectFile, this);
28818         
28819         this.notifyEl.hide();
28820         this.thumbEl.show();
28821         this.footerEl.show();
28822         
28823         this.baseRotateLevel();
28824         
28825         if(this.isDocument){
28826             this.setThumbBoxSize();
28827         }
28828         
28829         this.setThumbBoxPosition();
28830         
28831         this.baseScaleLevel();
28832         
28833         this.draw();
28834         
28835         this.resize();
28836         
28837         this.canvasLoaded = true;
28838         
28839         if(this.loadMask){
28840             this.maskEl.unmask();
28841         }
28842         
28843     },
28844     
28845     setCanvasPosition : function()
28846     {   
28847         if(!this.canvasEl){
28848             return;
28849         }
28850         
28851         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28852         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28853         
28854         this.previewEl.setLeft(pw);
28855         this.previewEl.setTop(ph);
28856         
28857     },
28858     
28859     onMouseDown : function(e)
28860     {   
28861         e.stopEvent();
28862         
28863         this.dragable = true;
28864         this.pinching = false;
28865         
28866         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28867             this.dragable = false;
28868             return;
28869         }
28870         
28871         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28872         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28873         
28874     },
28875     
28876     onMouseMove : function(e)
28877     {   
28878         e.stopEvent();
28879         
28880         if(!this.canvasLoaded){
28881             return;
28882         }
28883         
28884         if (!this.dragable){
28885             return;
28886         }
28887         
28888         var minX = Math.ceil(this.thumbEl.getLeft(true));
28889         var minY = Math.ceil(this.thumbEl.getTop(true));
28890         
28891         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28892         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28893         
28894         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28895         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28896         
28897         x = x - this.mouseX;
28898         y = y - this.mouseY;
28899         
28900         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28901         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28902         
28903         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28904         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28905         
28906         this.previewEl.setLeft(bgX);
28907         this.previewEl.setTop(bgY);
28908         
28909         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28910         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28911     },
28912     
28913     onMouseUp : function(e)
28914     {   
28915         e.stopEvent();
28916         
28917         this.dragable = false;
28918     },
28919     
28920     onMouseWheel : function(e)
28921     {   
28922         e.stopEvent();
28923         
28924         this.startScale = this.scale;
28925         
28926         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28927         
28928         if(!this.zoomable()){
28929             this.scale = this.startScale;
28930             return;
28931         }
28932         
28933         this.draw();
28934         
28935         return;
28936     },
28937     
28938     zoomable : function()
28939     {
28940         var minScale = this.thumbEl.getWidth() / this.minWidth;
28941         
28942         if(this.minWidth < this.minHeight){
28943             minScale = this.thumbEl.getHeight() / this.minHeight;
28944         }
28945         
28946         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28947         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28948         
28949         if(
28950                 this.isDocument &&
28951                 (this.rotate == 0 || this.rotate == 180) && 
28952                 (
28953                     width > this.imageEl.OriginWidth || 
28954                     height > this.imageEl.OriginHeight ||
28955                     (width < this.minWidth && height < this.minHeight)
28956                 )
28957         ){
28958             return false;
28959         }
28960         
28961         if(
28962                 this.isDocument &&
28963                 (this.rotate == 90 || this.rotate == 270) && 
28964                 (
28965                     width > this.imageEl.OriginWidth || 
28966                     height > this.imageEl.OriginHeight ||
28967                     (width < this.minHeight && height < this.minWidth)
28968                 )
28969         ){
28970             return false;
28971         }
28972         
28973         if(
28974                 !this.isDocument &&
28975                 (this.rotate == 0 || this.rotate == 180) && 
28976                 (
28977                     width < this.minWidth || 
28978                     width > this.imageEl.OriginWidth || 
28979                     height < this.minHeight || 
28980                     height > this.imageEl.OriginHeight
28981                 )
28982         ){
28983             return false;
28984         }
28985         
28986         if(
28987                 !this.isDocument &&
28988                 (this.rotate == 90 || this.rotate == 270) && 
28989                 (
28990                     width < this.minHeight || 
28991                     width > this.imageEl.OriginWidth || 
28992                     height < this.minWidth || 
28993                     height > this.imageEl.OriginHeight
28994                 )
28995         ){
28996             return false;
28997         }
28998         
28999         return true;
29000         
29001     },
29002     
29003     onRotateLeft : function(e)
29004     {   
29005         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29006             
29007             var minScale = this.thumbEl.getWidth() / this.minWidth;
29008             
29009             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29010             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29011             
29012             this.startScale = this.scale;
29013             
29014             while (this.getScaleLevel() < minScale){
29015             
29016                 this.scale = this.scale + 1;
29017                 
29018                 if(!this.zoomable()){
29019                     break;
29020                 }
29021                 
29022                 if(
29023                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29024                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29025                 ){
29026                     continue;
29027                 }
29028                 
29029                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29030
29031                 this.draw();
29032                 
29033                 return;
29034             }
29035             
29036             this.scale = this.startScale;
29037             
29038             this.onRotateFail();
29039             
29040             return false;
29041         }
29042         
29043         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29044
29045         if(this.isDocument){
29046             this.setThumbBoxSize();
29047             this.setThumbBoxPosition();
29048             this.setCanvasPosition();
29049         }
29050         
29051         this.draw();
29052         
29053         this.fireEvent('rotate', this, 'left');
29054         
29055     },
29056     
29057     onRotateRight : function(e)
29058     {
29059         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29060             
29061             var minScale = this.thumbEl.getWidth() / this.minWidth;
29062         
29063             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29064             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29065             
29066             this.startScale = this.scale;
29067             
29068             while (this.getScaleLevel() < minScale){
29069             
29070                 this.scale = this.scale + 1;
29071                 
29072                 if(!this.zoomable()){
29073                     break;
29074                 }
29075                 
29076                 if(
29077                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29078                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29079                 ){
29080                     continue;
29081                 }
29082                 
29083                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29084
29085                 this.draw();
29086                 
29087                 return;
29088             }
29089             
29090             this.scale = this.startScale;
29091             
29092             this.onRotateFail();
29093             
29094             return false;
29095         }
29096         
29097         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29098
29099         if(this.isDocument){
29100             this.setThumbBoxSize();
29101             this.setThumbBoxPosition();
29102             this.setCanvasPosition();
29103         }
29104         
29105         this.draw();
29106         
29107         this.fireEvent('rotate', this, 'right');
29108     },
29109     
29110     onRotateFail : function()
29111     {
29112         this.errorEl.show(true);
29113         
29114         var _this = this;
29115         
29116         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29117     },
29118     
29119     draw : function()
29120     {
29121         this.previewEl.dom.innerHTML = '';
29122         
29123         var canvasEl = document.createElement("canvas");
29124         
29125         var contextEl = canvasEl.getContext("2d");
29126         
29127         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29128         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29129         var center = this.imageEl.OriginWidth / 2;
29130         
29131         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29132             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29133             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29134             center = this.imageEl.OriginHeight / 2;
29135         }
29136         
29137         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29138         
29139         contextEl.translate(center, center);
29140         contextEl.rotate(this.rotate * Math.PI / 180);
29141
29142         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29143         
29144         this.canvasEl = document.createElement("canvas");
29145         
29146         this.contextEl = this.canvasEl.getContext("2d");
29147         
29148         switch (this.rotate) {
29149             case 0 :
29150                 
29151                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29152                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29153                 
29154                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29155                 
29156                 break;
29157             case 90 : 
29158                 
29159                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29160                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29161                 
29162                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29163                     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);
29164                     break;
29165                 }
29166                 
29167                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29168                 
29169                 break;
29170             case 180 :
29171                 
29172                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29173                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29174                 
29175                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29176                     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);
29177                     break;
29178                 }
29179                 
29180                 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);
29181                 
29182                 break;
29183             case 270 :
29184                 
29185                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29186                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29187         
29188                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29189                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29190                     break;
29191                 }
29192                 
29193                 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);
29194                 
29195                 break;
29196             default : 
29197                 break;
29198         }
29199         
29200         this.previewEl.appendChild(this.canvasEl);
29201         
29202         this.setCanvasPosition();
29203     },
29204     
29205     crop : function()
29206     {
29207         if(!this.canvasLoaded){
29208             return;
29209         }
29210         
29211         var imageCanvas = document.createElement("canvas");
29212         
29213         var imageContext = imageCanvas.getContext("2d");
29214         
29215         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29216         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29217         
29218         var center = imageCanvas.width / 2;
29219         
29220         imageContext.translate(center, center);
29221         
29222         imageContext.rotate(this.rotate * Math.PI / 180);
29223         
29224         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29225         
29226         var canvas = document.createElement("canvas");
29227         
29228         var context = canvas.getContext("2d");
29229                 
29230         canvas.width = this.minWidth;
29231         canvas.height = this.minHeight;
29232
29233         switch (this.rotate) {
29234             case 0 :
29235                 
29236                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29237                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29238                 
29239                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29240                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29241                 
29242                 var targetWidth = this.minWidth - 2 * x;
29243                 var targetHeight = this.minHeight - 2 * y;
29244                 
29245                 var scale = 1;
29246                 
29247                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29248                     scale = targetWidth / width;
29249                 }
29250                 
29251                 if(x > 0 && y == 0){
29252                     scale = targetHeight / height;
29253                 }
29254                 
29255                 if(x > 0 && y > 0){
29256                     scale = targetWidth / width;
29257                     
29258                     if(width < height){
29259                         scale = targetHeight / height;
29260                     }
29261                 }
29262                 
29263                 context.scale(scale, scale);
29264                 
29265                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29266                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29267
29268                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29269                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29270
29271                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29272                 
29273                 break;
29274             case 90 : 
29275                 
29276                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29277                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29278                 
29279                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29280                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29281                 
29282                 var targetWidth = this.minWidth - 2 * x;
29283                 var targetHeight = this.minHeight - 2 * y;
29284                 
29285                 var scale = 1;
29286                 
29287                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29288                     scale = targetWidth / width;
29289                 }
29290                 
29291                 if(x > 0 && y == 0){
29292                     scale = targetHeight / height;
29293                 }
29294                 
29295                 if(x > 0 && y > 0){
29296                     scale = targetWidth / width;
29297                     
29298                     if(width < height){
29299                         scale = targetHeight / height;
29300                     }
29301                 }
29302                 
29303                 context.scale(scale, scale);
29304                 
29305                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29306                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29307
29308                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29309                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29310                 
29311                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29312                 
29313                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29314                 
29315                 break;
29316             case 180 :
29317                 
29318                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29319                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29320                 
29321                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29322                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29323                 
29324                 var targetWidth = this.minWidth - 2 * x;
29325                 var targetHeight = this.minHeight - 2 * y;
29326                 
29327                 var scale = 1;
29328                 
29329                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29330                     scale = targetWidth / width;
29331                 }
29332                 
29333                 if(x > 0 && y == 0){
29334                     scale = targetHeight / height;
29335                 }
29336                 
29337                 if(x > 0 && y > 0){
29338                     scale = targetWidth / width;
29339                     
29340                     if(width < height){
29341                         scale = targetHeight / height;
29342                     }
29343                 }
29344                 
29345                 context.scale(scale, scale);
29346                 
29347                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29348                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29349
29350                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29351                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29352
29353                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29354                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29355                 
29356                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29357                 
29358                 break;
29359             case 270 :
29360                 
29361                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29362                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29363                 
29364                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29365                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29366                 
29367                 var targetWidth = this.minWidth - 2 * x;
29368                 var targetHeight = this.minHeight - 2 * y;
29369                 
29370                 var scale = 1;
29371                 
29372                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29373                     scale = targetWidth / width;
29374                 }
29375                 
29376                 if(x > 0 && y == 0){
29377                     scale = targetHeight / height;
29378                 }
29379                 
29380                 if(x > 0 && y > 0){
29381                     scale = targetWidth / width;
29382                     
29383                     if(width < height){
29384                         scale = targetHeight / height;
29385                     }
29386                 }
29387                 
29388                 context.scale(scale, scale);
29389                 
29390                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29391                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29392
29393                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29394                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29395                 
29396                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29397                 
29398                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29399                 
29400                 break;
29401             default : 
29402                 break;
29403         }
29404         
29405         this.cropData = canvas.toDataURL(this.cropType);
29406         
29407         if(this.fireEvent('crop', this, this.cropData) !== false){
29408             this.process(this.file, this.cropData);
29409         }
29410         
29411         return;
29412         
29413     },
29414     
29415     setThumbBoxSize : function()
29416     {
29417         var width, height;
29418         
29419         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29420             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29421             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29422             
29423             this.minWidth = width;
29424             this.minHeight = height;
29425             
29426             if(this.rotate == 90 || this.rotate == 270){
29427                 this.minWidth = height;
29428                 this.minHeight = width;
29429             }
29430         }
29431         
29432         height = 300;
29433         width = Math.ceil(this.minWidth * height / this.minHeight);
29434         
29435         if(this.minWidth > this.minHeight){
29436             width = 300;
29437             height = Math.ceil(this.minHeight * width / this.minWidth);
29438         }
29439         
29440         this.thumbEl.setStyle({
29441             width : width + 'px',
29442             height : height + 'px'
29443         });
29444
29445         return;
29446             
29447     },
29448     
29449     setThumbBoxPosition : function()
29450     {
29451         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29452         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29453         
29454         this.thumbEl.setLeft(x);
29455         this.thumbEl.setTop(y);
29456         
29457     },
29458     
29459     baseRotateLevel : function()
29460     {
29461         this.baseRotate = 1;
29462         
29463         if(
29464                 typeof(this.exif) != 'undefined' &&
29465                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29466                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29467         ){
29468             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29469         }
29470         
29471         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29472         
29473     },
29474     
29475     baseScaleLevel : function()
29476     {
29477         var width, height;
29478         
29479         if(this.isDocument){
29480             
29481             if(this.baseRotate == 6 || this.baseRotate == 8){
29482             
29483                 height = this.thumbEl.getHeight();
29484                 this.baseScale = height / this.imageEl.OriginWidth;
29485
29486                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29487                     width = this.thumbEl.getWidth();
29488                     this.baseScale = width / this.imageEl.OriginHeight;
29489                 }
29490
29491                 return;
29492             }
29493
29494             height = this.thumbEl.getHeight();
29495             this.baseScale = height / this.imageEl.OriginHeight;
29496
29497             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29498                 width = this.thumbEl.getWidth();
29499                 this.baseScale = width / this.imageEl.OriginWidth;
29500             }
29501
29502             return;
29503         }
29504         
29505         if(this.baseRotate == 6 || this.baseRotate == 8){
29506             
29507             width = this.thumbEl.getHeight();
29508             this.baseScale = width / this.imageEl.OriginHeight;
29509             
29510             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29511                 height = this.thumbEl.getWidth();
29512                 this.baseScale = height / this.imageEl.OriginHeight;
29513             }
29514             
29515             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29516                 height = this.thumbEl.getWidth();
29517                 this.baseScale = height / this.imageEl.OriginHeight;
29518                 
29519                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29520                     width = this.thumbEl.getHeight();
29521                     this.baseScale = width / this.imageEl.OriginWidth;
29522                 }
29523             }
29524             
29525             return;
29526         }
29527         
29528         width = this.thumbEl.getWidth();
29529         this.baseScale = width / this.imageEl.OriginWidth;
29530         
29531         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29532             height = this.thumbEl.getHeight();
29533             this.baseScale = height / this.imageEl.OriginHeight;
29534         }
29535         
29536         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29537             
29538             height = this.thumbEl.getHeight();
29539             this.baseScale = height / this.imageEl.OriginHeight;
29540             
29541             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29542                 width = this.thumbEl.getWidth();
29543                 this.baseScale = width / this.imageEl.OriginWidth;
29544             }
29545             
29546         }
29547         
29548         return;
29549     },
29550     
29551     getScaleLevel : function()
29552     {
29553         return this.baseScale * Math.pow(1.1, this.scale);
29554     },
29555     
29556     onTouchStart : function(e)
29557     {
29558         if(!this.canvasLoaded){
29559             this.beforeSelectFile(e);
29560             return;
29561         }
29562         
29563         var touches = e.browserEvent.touches;
29564         
29565         if(!touches){
29566             return;
29567         }
29568         
29569         if(touches.length == 1){
29570             this.onMouseDown(e);
29571             return;
29572         }
29573         
29574         if(touches.length != 2){
29575             return;
29576         }
29577         
29578         var coords = [];
29579         
29580         for(var i = 0, finger; finger = touches[i]; i++){
29581             coords.push(finger.pageX, finger.pageY);
29582         }
29583         
29584         var x = Math.pow(coords[0] - coords[2], 2);
29585         var y = Math.pow(coords[1] - coords[3], 2);
29586         
29587         this.startDistance = Math.sqrt(x + y);
29588         
29589         this.startScale = this.scale;
29590         
29591         this.pinching = true;
29592         this.dragable = false;
29593         
29594     },
29595     
29596     onTouchMove : function(e)
29597     {
29598         if(!this.pinching && !this.dragable){
29599             return;
29600         }
29601         
29602         var touches = e.browserEvent.touches;
29603         
29604         if(!touches){
29605             return;
29606         }
29607         
29608         if(this.dragable){
29609             this.onMouseMove(e);
29610             return;
29611         }
29612         
29613         var coords = [];
29614         
29615         for(var i = 0, finger; finger = touches[i]; i++){
29616             coords.push(finger.pageX, finger.pageY);
29617         }
29618         
29619         var x = Math.pow(coords[0] - coords[2], 2);
29620         var y = Math.pow(coords[1] - coords[3], 2);
29621         
29622         this.endDistance = Math.sqrt(x + y);
29623         
29624         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29625         
29626         if(!this.zoomable()){
29627             this.scale = this.startScale;
29628             return;
29629         }
29630         
29631         this.draw();
29632         
29633     },
29634     
29635     onTouchEnd : function(e)
29636     {
29637         this.pinching = false;
29638         this.dragable = false;
29639         
29640     },
29641     
29642     process : function(file, crop)
29643     {
29644         if(this.loadMask){
29645             this.maskEl.mask(this.loadingText);
29646         }
29647         
29648         this.xhr = new XMLHttpRequest();
29649         
29650         file.xhr = this.xhr;
29651
29652         this.xhr.open(this.method, this.url, true);
29653         
29654         var headers = {
29655             "Accept": "application/json",
29656             "Cache-Control": "no-cache",
29657             "X-Requested-With": "XMLHttpRequest"
29658         };
29659         
29660         for (var headerName in headers) {
29661             var headerValue = headers[headerName];
29662             if (headerValue) {
29663                 this.xhr.setRequestHeader(headerName, headerValue);
29664             }
29665         }
29666         
29667         var _this = this;
29668         
29669         this.xhr.onload = function()
29670         {
29671             _this.xhrOnLoad(_this.xhr);
29672         }
29673         
29674         this.xhr.onerror = function()
29675         {
29676             _this.xhrOnError(_this.xhr);
29677         }
29678         
29679         var formData = new FormData();
29680
29681         formData.append('returnHTML', 'NO');
29682         
29683         if(crop){
29684             formData.append('crop', crop);
29685         }
29686         
29687         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29688             formData.append(this.paramName, file, file.name);
29689         }
29690         
29691         if(typeof(file.filename) != 'undefined'){
29692             formData.append('filename', file.filename);
29693         }
29694         
29695         if(typeof(file.mimetype) != 'undefined'){
29696             formData.append('mimetype', file.mimetype);
29697         }
29698         
29699         if(this.fireEvent('arrange', this, formData) != false){
29700             this.xhr.send(formData);
29701         };
29702     },
29703     
29704     xhrOnLoad : function(xhr)
29705     {
29706         if(this.loadMask){
29707             this.maskEl.unmask();
29708         }
29709         
29710         if (xhr.readyState !== 4) {
29711             this.fireEvent('exception', this, xhr);
29712             return;
29713         }
29714
29715         var response = Roo.decode(xhr.responseText);
29716         
29717         if(!response.success){
29718             this.fireEvent('exception', this, xhr);
29719             return;
29720         }
29721         
29722         var response = Roo.decode(xhr.responseText);
29723         
29724         this.fireEvent('upload', this, response);
29725         
29726     },
29727     
29728     xhrOnError : function()
29729     {
29730         if(this.loadMask){
29731             this.maskEl.unmask();
29732         }
29733         
29734         Roo.log('xhr on error');
29735         
29736         var response = Roo.decode(xhr.responseText);
29737           
29738         Roo.log(response);
29739         
29740     },
29741     
29742     prepare : function(file)
29743     {   
29744         if(this.loadMask){
29745             this.maskEl.mask(this.loadingText);
29746         }
29747         
29748         this.file = false;
29749         this.exif = {};
29750         
29751         if(typeof(file) === 'string'){
29752             this.loadCanvas(file);
29753             return;
29754         }
29755         
29756         if(!file || !this.urlAPI){
29757             return;
29758         }
29759         
29760         this.file = file;
29761         this.cropType = file.type;
29762         
29763         var _this = this;
29764         
29765         if(this.fireEvent('prepare', this, this.file) != false){
29766             
29767             var reader = new FileReader();
29768             
29769             reader.onload = function (e) {
29770                 if (e.target.error) {
29771                     Roo.log(e.target.error);
29772                     return;
29773                 }
29774                 
29775                 var buffer = e.target.result,
29776                     dataView = new DataView(buffer),
29777                     offset = 2,
29778                     maxOffset = dataView.byteLength - 4,
29779                     markerBytes,
29780                     markerLength;
29781                 
29782                 if (dataView.getUint16(0) === 0xffd8) {
29783                     while (offset < maxOffset) {
29784                         markerBytes = dataView.getUint16(offset);
29785                         
29786                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29787                             markerLength = dataView.getUint16(offset + 2) + 2;
29788                             if (offset + markerLength > dataView.byteLength) {
29789                                 Roo.log('Invalid meta data: Invalid segment size.');
29790                                 break;
29791                             }
29792                             
29793                             if(markerBytes == 0xffe1){
29794                                 _this.parseExifData(
29795                                     dataView,
29796                                     offset,
29797                                     markerLength
29798                                 );
29799                             }
29800                             
29801                             offset += markerLength;
29802                             
29803                             continue;
29804                         }
29805                         
29806                         break;
29807                     }
29808                     
29809                 }
29810                 
29811                 var url = _this.urlAPI.createObjectURL(_this.file);
29812                 
29813                 _this.loadCanvas(url);
29814                 
29815                 return;
29816             }
29817             
29818             reader.readAsArrayBuffer(this.file);
29819             
29820         }
29821         
29822     },
29823     
29824     parseExifData : function(dataView, offset, length)
29825     {
29826         var tiffOffset = offset + 10,
29827             littleEndian,
29828             dirOffset;
29829     
29830         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29831             // No Exif data, might be XMP data instead
29832             return;
29833         }
29834         
29835         // Check for the ASCII code for "Exif" (0x45786966):
29836         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29837             // No Exif data, might be XMP data instead
29838             return;
29839         }
29840         if (tiffOffset + 8 > dataView.byteLength) {
29841             Roo.log('Invalid Exif data: Invalid segment size.');
29842             return;
29843         }
29844         // Check for the two null bytes:
29845         if (dataView.getUint16(offset + 8) !== 0x0000) {
29846             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29847             return;
29848         }
29849         // Check the byte alignment:
29850         switch (dataView.getUint16(tiffOffset)) {
29851         case 0x4949:
29852             littleEndian = true;
29853             break;
29854         case 0x4D4D:
29855             littleEndian = false;
29856             break;
29857         default:
29858             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29859             return;
29860         }
29861         // Check for the TIFF tag marker (0x002A):
29862         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29863             Roo.log('Invalid Exif data: Missing TIFF marker.');
29864             return;
29865         }
29866         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29867         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29868         
29869         this.parseExifTags(
29870             dataView,
29871             tiffOffset,
29872             tiffOffset + dirOffset,
29873             littleEndian
29874         );
29875     },
29876     
29877     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29878     {
29879         var tagsNumber,
29880             dirEndOffset,
29881             i;
29882         if (dirOffset + 6 > dataView.byteLength) {
29883             Roo.log('Invalid Exif data: Invalid directory offset.');
29884             return;
29885         }
29886         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29887         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29888         if (dirEndOffset + 4 > dataView.byteLength) {
29889             Roo.log('Invalid Exif data: Invalid directory size.');
29890             return;
29891         }
29892         for (i = 0; i < tagsNumber; i += 1) {
29893             this.parseExifTag(
29894                 dataView,
29895                 tiffOffset,
29896                 dirOffset + 2 + 12 * i, // tag offset
29897                 littleEndian
29898             );
29899         }
29900         // Return the offset to the next directory:
29901         return dataView.getUint32(dirEndOffset, littleEndian);
29902     },
29903     
29904     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29905     {
29906         var tag = dataView.getUint16(offset, littleEndian);
29907         
29908         this.exif[tag] = this.getExifValue(
29909             dataView,
29910             tiffOffset,
29911             offset,
29912             dataView.getUint16(offset + 2, littleEndian), // tag type
29913             dataView.getUint32(offset + 4, littleEndian), // tag length
29914             littleEndian
29915         );
29916     },
29917     
29918     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29919     {
29920         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29921             tagSize,
29922             dataOffset,
29923             values,
29924             i,
29925             str,
29926             c;
29927     
29928         if (!tagType) {
29929             Roo.log('Invalid Exif data: Invalid tag type.');
29930             return;
29931         }
29932         
29933         tagSize = tagType.size * length;
29934         // Determine if the value is contained in the dataOffset bytes,
29935         // or if the value at the dataOffset is a pointer to the actual data:
29936         dataOffset = tagSize > 4 ?
29937                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29938         if (dataOffset + tagSize > dataView.byteLength) {
29939             Roo.log('Invalid Exif data: Invalid data offset.');
29940             return;
29941         }
29942         if (length === 1) {
29943             return tagType.getValue(dataView, dataOffset, littleEndian);
29944         }
29945         values = [];
29946         for (i = 0; i < length; i += 1) {
29947             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29948         }
29949         
29950         if (tagType.ascii) {
29951             str = '';
29952             // Concatenate the chars:
29953             for (i = 0; i < values.length; i += 1) {
29954                 c = values[i];
29955                 // Ignore the terminating NULL byte(s):
29956                 if (c === '\u0000') {
29957                     break;
29958                 }
29959                 str += c;
29960             }
29961             return str;
29962         }
29963         return values;
29964     }
29965     
29966 });
29967
29968 Roo.apply(Roo.bootstrap.UploadCropbox, {
29969     tags : {
29970         'Orientation': 0x0112
29971     },
29972     
29973     Orientation: {
29974             1: 0, //'top-left',
29975 //            2: 'top-right',
29976             3: 180, //'bottom-right',
29977 //            4: 'bottom-left',
29978 //            5: 'left-top',
29979             6: 90, //'right-top',
29980 //            7: 'right-bottom',
29981             8: 270 //'left-bottom'
29982     },
29983     
29984     exifTagTypes : {
29985         // byte, 8-bit unsigned int:
29986         1: {
29987             getValue: function (dataView, dataOffset) {
29988                 return dataView.getUint8(dataOffset);
29989             },
29990             size: 1
29991         },
29992         // ascii, 8-bit byte:
29993         2: {
29994             getValue: function (dataView, dataOffset) {
29995                 return String.fromCharCode(dataView.getUint8(dataOffset));
29996             },
29997             size: 1,
29998             ascii: true
29999         },
30000         // short, 16 bit int:
30001         3: {
30002             getValue: function (dataView, dataOffset, littleEndian) {
30003                 return dataView.getUint16(dataOffset, littleEndian);
30004             },
30005             size: 2
30006         },
30007         // long, 32 bit int:
30008         4: {
30009             getValue: function (dataView, dataOffset, littleEndian) {
30010                 return dataView.getUint32(dataOffset, littleEndian);
30011             },
30012             size: 4
30013         },
30014         // rational = two long values, first is numerator, second is denominator:
30015         5: {
30016             getValue: function (dataView, dataOffset, littleEndian) {
30017                 return dataView.getUint32(dataOffset, littleEndian) /
30018                     dataView.getUint32(dataOffset + 4, littleEndian);
30019             },
30020             size: 8
30021         },
30022         // slong, 32 bit signed int:
30023         9: {
30024             getValue: function (dataView, dataOffset, littleEndian) {
30025                 return dataView.getInt32(dataOffset, littleEndian);
30026             },
30027             size: 4
30028         },
30029         // srational, two slongs, first is numerator, second is denominator:
30030         10: {
30031             getValue: function (dataView, dataOffset, littleEndian) {
30032                 return dataView.getInt32(dataOffset, littleEndian) /
30033                     dataView.getInt32(dataOffset + 4, littleEndian);
30034             },
30035             size: 8
30036         }
30037     },
30038     
30039     footer : {
30040         STANDARD : [
30041             {
30042                 tag : 'div',
30043                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30044                 action : 'rotate-left',
30045                 cn : [
30046                     {
30047                         tag : 'button',
30048                         cls : 'btn btn-default',
30049                         html : '<i class="fa fa-undo"></i>'
30050                     }
30051                 ]
30052             },
30053             {
30054                 tag : 'div',
30055                 cls : 'btn-group roo-upload-cropbox-picture',
30056                 action : 'picture',
30057                 cn : [
30058                     {
30059                         tag : 'button',
30060                         cls : 'btn btn-default',
30061                         html : '<i class="fa fa-picture-o"></i>'
30062                     }
30063                 ]
30064             },
30065             {
30066                 tag : 'div',
30067                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30068                 action : 'rotate-right',
30069                 cn : [
30070                     {
30071                         tag : 'button',
30072                         cls : 'btn btn-default',
30073                         html : '<i class="fa fa-repeat"></i>'
30074                     }
30075                 ]
30076             }
30077         ],
30078         DOCUMENT : [
30079             {
30080                 tag : 'div',
30081                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30082                 action : 'rotate-left',
30083                 cn : [
30084                     {
30085                         tag : 'button',
30086                         cls : 'btn btn-default',
30087                         html : '<i class="fa fa-undo"></i>'
30088                     }
30089                 ]
30090             },
30091             {
30092                 tag : 'div',
30093                 cls : 'btn-group roo-upload-cropbox-download',
30094                 action : 'download',
30095                 cn : [
30096                     {
30097                         tag : 'button',
30098                         cls : 'btn btn-default',
30099                         html : '<i class="fa fa-download"></i>'
30100                     }
30101                 ]
30102             },
30103             {
30104                 tag : 'div',
30105                 cls : 'btn-group roo-upload-cropbox-crop',
30106                 action : 'crop',
30107                 cn : [
30108                     {
30109                         tag : 'button',
30110                         cls : 'btn btn-default',
30111                         html : '<i class="fa fa-crop"></i>'
30112                     }
30113                 ]
30114             },
30115             {
30116                 tag : 'div',
30117                 cls : 'btn-group roo-upload-cropbox-trash',
30118                 action : 'trash',
30119                 cn : [
30120                     {
30121                         tag : 'button',
30122                         cls : 'btn btn-default',
30123                         html : '<i class="fa fa-trash"></i>'
30124                     }
30125                 ]
30126             },
30127             {
30128                 tag : 'div',
30129                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30130                 action : 'rotate-right',
30131                 cn : [
30132                     {
30133                         tag : 'button',
30134                         cls : 'btn btn-default',
30135                         html : '<i class="fa fa-repeat"></i>'
30136                     }
30137                 ]
30138             }
30139         ],
30140         ROTATOR : [
30141             {
30142                 tag : 'div',
30143                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30144                 action : 'rotate-left',
30145                 cn : [
30146                     {
30147                         tag : 'button',
30148                         cls : 'btn btn-default',
30149                         html : '<i class="fa fa-undo"></i>'
30150                     }
30151                 ]
30152             },
30153             {
30154                 tag : 'div',
30155                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30156                 action : 'rotate-right',
30157                 cn : [
30158                     {
30159                         tag : 'button',
30160                         cls : 'btn btn-default',
30161                         html : '<i class="fa fa-repeat"></i>'
30162                     }
30163                 ]
30164             }
30165         ]
30166     }
30167 });
30168
30169 /*
30170 * Licence: LGPL
30171 */
30172
30173 /**
30174  * @class Roo.bootstrap.DocumentManager
30175  * @extends Roo.bootstrap.Component
30176  * Bootstrap DocumentManager class
30177  * @cfg {String} paramName default 'imageUpload'
30178  * @cfg {String} toolTipName default 'filename'
30179  * @cfg {String} method default POST
30180  * @cfg {String} url action url
30181  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30182  * @cfg {Boolean} multiple multiple upload default true
30183  * @cfg {Number} thumbSize default 300
30184  * @cfg {String} fieldLabel
30185  * @cfg {Number} labelWidth default 4
30186  * @cfg {String} labelAlign (left|top) default left
30187  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30188 * @cfg {Number} labellg set the width of label (1-12)
30189  * @cfg {Number} labelmd set the width of label (1-12)
30190  * @cfg {Number} labelsm set the width of label (1-12)
30191  * @cfg {Number} labelxs set the width of label (1-12)
30192  * 
30193  * @constructor
30194  * Create a new DocumentManager
30195  * @param {Object} config The config object
30196  */
30197
30198 Roo.bootstrap.DocumentManager = function(config){
30199     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30200     
30201     this.files = [];
30202     this.delegates = [];
30203     
30204     this.addEvents({
30205         /**
30206          * @event initial
30207          * Fire when initial the DocumentManager
30208          * @param {Roo.bootstrap.DocumentManager} this
30209          */
30210         "initial" : true,
30211         /**
30212          * @event inspect
30213          * inspect selected file
30214          * @param {Roo.bootstrap.DocumentManager} this
30215          * @param {File} file
30216          */
30217         "inspect" : true,
30218         /**
30219          * @event exception
30220          * Fire when xhr load exception
30221          * @param {Roo.bootstrap.DocumentManager} this
30222          * @param {XMLHttpRequest} xhr
30223          */
30224         "exception" : true,
30225         /**
30226          * @event afterupload
30227          * Fire when xhr load exception
30228          * @param {Roo.bootstrap.DocumentManager} this
30229          * @param {XMLHttpRequest} xhr
30230          */
30231         "afterupload" : true,
30232         /**
30233          * @event prepare
30234          * prepare the form data
30235          * @param {Roo.bootstrap.DocumentManager} this
30236          * @param {Object} formData
30237          */
30238         "prepare" : true,
30239         /**
30240          * @event remove
30241          * Fire when remove the file
30242          * @param {Roo.bootstrap.DocumentManager} this
30243          * @param {Object} file
30244          */
30245         "remove" : true,
30246         /**
30247          * @event refresh
30248          * Fire after refresh the file
30249          * @param {Roo.bootstrap.DocumentManager} this
30250          */
30251         "refresh" : true,
30252         /**
30253          * @event click
30254          * Fire after click the image
30255          * @param {Roo.bootstrap.DocumentManager} this
30256          * @param {Object} file
30257          */
30258         "click" : true,
30259         /**
30260          * @event edit
30261          * Fire when upload a image and editable set to true
30262          * @param {Roo.bootstrap.DocumentManager} this
30263          * @param {Object} file
30264          */
30265         "edit" : true,
30266         /**
30267          * @event beforeselectfile
30268          * Fire before select file
30269          * @param {Roo.bootstrap.DocumentManager} this
30270          */
30271         "beforeselectfile" : true,
30272         /**
30273          * @event process
30274          * Fire before process file
30275          * @param {Roo.bootstrap.DocumentManager} this
30276          * @param {Object} file
30277          */
30278         "process" : true,
30279         /**
30280          * @event previewrendered
30281          * Fire when preview rendered
30282          * @param {Roo.bootstrap.DocumentManager} this
30283          * @param {Object} file
30284          */
30285         "previewrendered" : true,
30286         /**
30287          */
30288         "previewResize" : true
30289         
30290     });
30291 };
30292
30293 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30294     
30295     boxes : 0,
30296     inputName : '',
30297     thumbSize : 300,
30298     multiple : true,
30299     files : false,
30300     method : 'POST',
30301     url : '',
30302     paramName : 'imageUpload',
30303     toolTipName : 'filename',
30304     fieldLabel : '',
30305     labelWidth : 4,
30306     labelAlign : 'left',
30307     editable : true,
30308     delegates : false,
30309     xhr : false, 
30310     
30311     labellg : 0,
30312     labelmd : 0,
30313     labelsm : 0,
30314     labelxs : 0,
30315     
30316     getAutoCreate : function()
30317     {   
30318         var managerWidget = {
30319             tag : 'div',
30320             cls : 'roo-document-manager',
30321             cn : [
30322                 {
30323                     tag : 'input',
30324                     cls : 'roo-document-manager-selector',
30325                     type : 'file'
30326                 },
30327                 {
30328                     tag : 'div',
30329                     cls : 'roo-document-manager-uploader',
30330                     cn : [
30331                         {
30332                             tag : 'div',
30333                             cls : 'roo-document-manager-upload-btn',
30334                             html : '<i class="fa fa-plus"></i>'
30335                         }
30336                     ]
30337                     
30338                 }
30339             ]
30340         };
30341         
30342         var content = [
30343             {
30344                 tag : 'div',
30345                 cls : 'column col-md-12',
30346                 cn : managerWidget
30347             }
30348         ];
30349         
30350         if(this.fieldLabel.length){
30351             
30352             content = [
30353                 {
30354                     tag : 'div',
30355                     cls : 'column col-md-12',
30356                     html : this.fieldLabel
30357                 },
30358                 {
30359                     tag : 'div',
30360                     cls : 'column col-md-12',
30361                     cn : managerWidget
30362                 }
30363             ];
30364
30365             if(this.labelAlign == 'left'){
30366                 content = [
30367                     {
30368                         tag : 'div',
30369                         cls : 'column',
30370                         html : this.fieldLabel
30371                     },
30372                     {
30373                         tag : 'div',
30374                         cls : 'column',
30375                         cn : managerWidget
30376                     }
30377                 ];
30378                 
30379                 if(this.labelWidth > 12){
30380                     content[0].style = "width: " + this.labelWidth + 'px';
30381                 }
30382
30383                 if(this.labelWidth < 13 && this.labelmd == 0){
30384                     this.labelmd = this.labelWidth;
30385                 }
30386
30387                 if(this.labellg > 0){
30388                     content[0].cls += ' col-lg-' + this.labellg;
30389                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30390                 }
30391
30392                 if(this.labelmd > 0){
30393                     content[0].cls += ' col-md-' + this.labelmd;
30394                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30395                 }
30396
30397                 if(this.labelsm > 0){
30398                     content[0].cls += ' col-sm-' + this.labelsm;
30399                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30400                 }
30401
30402                 if(this.labelxs > 0){
30403                     content[0].cls += ' col-xs-' + this.labelxs;
30404                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30405                 }
30406                 
30407             }
30408         }
30409         
30410         var cfg = {
30411             tag : 'div',
30412             cls : 'row clearfix',
30413             cn : content
30414         };
30415         
30416         return cfg;
30417         
30418     },
30419     
30420     initEvents : function()
30421     {
30422         this.managerEl = this.el.select('.roo-document-manager', true).first();
30423         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30424         
30425         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30426         this.selectorEl.hide();
30427         
30428         if(this.multiple){
30429             this.selectorEl.attr('multiple', 'multiple');
30430         }
30431         
30432         this.selectorEl.on('change', this.onFileSelected, this);
30433         
30434         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30435         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30436         
30437         this.uploader.on('click', this.onUploaderClick, this);
30438         
30439         this.renderProgressDialog();
30440         
30441         var _this = this;
30442         
30443         window.addEventListener("resize", function() { _this.refresh(); } );
30444         
30445         this.fireEvent('initial', this);
30446     },
30447     
30448     renderProgressDialog : function()
30449     {
30450         var _this = this;
30451         
30452         this.progressDialog = new Roo.bootstrap.Modal({
30453             cls : 'roo-document-manager-progress-dialog',
30454             allow_close : false,
30455             animate : false,
30456             title : '',
30457             buttons : [
30458                 {
30459                     name  :'cancel',
30460                     weight : 'danger',
30461                     html : 'Cancel'
30462                 }
30463             ], 
30464             listeners : { 
30465                 btnclick : function() {
30466                     _this.uploadCancel();
30467                     this.hide();
30468                 }
30469             }
30470         });
30471          
30472         this.progressDialog.render(Roo.get(document.body));
30473          
30474         this.progress = new Roo.bootstrap.Progress({
30475             cls : 'roo-document-manager-progress',
30476             active : true,
30477             striped : true
30478         });
30479         
30480         this.progress.render(this.progressDialog.getChildContainer());
30481         
30482         this.progressBar = new Roo.bootstrap.ProgressBar({
30483             cls : 'roo-document-manager-progress-bar',
30484             aria_valuenow : 0,
30485             aria_valuemin : 0,
30486             aria_valuemax : 12,
30487             panel : 'success'
30488         });
30489         
30490         this.progressBar.render(this.progress.getChildContainer());
30491     },
30492     
30493     onUploaderClick : function(e)
30494     {
30495         e.preventDefault();
30496      
30497         if(this.fireEvent('beforeselectfile', this) != false){
30498             this.selectorEl.dom.click();
30499         }
30500         
30501     },
30502     
30503     onFileSelected : function(e)
30504     {
30505         e.preventDefault();
30506         
30507         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30508             return;
30509         }
30510         
30511         Roo.each(this.selectorEl.dom.files, function(file){
30512             if(this.fireEvent('inspect', this, file) != false){
30513                 this.files.push(file);
30514             }
30515         }, this);
30516         
30517         this.queue();
30518         
30519     },
30520     
30521     queue : function()
30522     {
30523         this.selectorEl.dom.value = '';
30524         
30525         if(!this.files || !this.files.length){
30526             return;
30527         }
30528         
30529         if(this.boxes > 0 && this.files.length > this.boxes){
30530             this.files = this.files.slice(0, this.boxes);
30531         }
30532         
30533         this.uploader.show();
30534         
30535         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30536             this.uploader.hide();
30537         }
30538         
30539         var _this = this;
30540         
30541         var files = [];
30542         
30543         var docs = [];
30544         
30545         Roo.each(this.files, function(file){
30546             
30547             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30548                 var f = this.renderPreview(file);
30549                 files.push(f);
30550                 return;
30551             }
30552             
30553             if(file.type.indexOf('image') != -1){
30554                 this.delegates.push(
30555                     (function(){
30556                         _this.process(file);
30557                     }).createDelegate(this)
30558                 );
30559         
30560                 return;
30561             }
30562             
30563             docs.push(
30564                 (function(){
30565                     _this.process(file);
30566                 }).createDelegate(this)
30567             );
30568             
30569         }, this);
30570         
30571         this.files = files;
30572         
30573         this.delegates = this.delegates.concat(docs);
30574         
30575         if(!this.delegates.length){
30576             this.refresh();
30577             return;
30578         }
30579         
30580         this.progressBar.aria_valuemax = this.delegates.length;
30581         
30582         this.arrange();
30583         
30584         return;
30585     },
30586     
30587     arrange : function()
30588     {
30589         if(!this.delegates.length){
30590             this.progressDialog.hide();
30591             this.refresh();
30592             return;
30593         }
30594         
30595         var delegate = this.delegates.shift();
30596         
30597         this.progressDialog.show();
30598         
30599         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30600         
30601         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30602         
30603         delegate();
30604     },
30605     
30606     refresh : function()
30607     {
30608         this.uploader.show();
30609         
30610         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30611             this.uploader.hide();
30612         }
30613         
30614         Roo.isTouch ? this.closable(false) : this.closable(true);
30615         
30616         this.fireEvent('refresh', this);
30617     },
30618     
30619     onRemove : function(e, el, o)
30620     {
30621         e.preventDefault();
30622         
30623         this.fireEvent('remove', this, o);
30624         
30625     },
30626     
30627     remove : function(o)
30628     {
30629         var files = [];
30630         
30631         Roo.each(this.files, function(file){
30632             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30633                 files.push(file);
30634                 return;
30635             }
30636
30637             o.target.remove();
30638
30639         }, this);
30640         
30641         this.files = files;
30642         
30643         this.refresh();
30644     },
30645     
30646     clear : function()
30647     {
30648         Roo.each(this.files, function(file){
30649             if(!file.target){
30650                 return;
30651             }
30652             
30653             file.target.remove();
30654
30655         }, this);
30656         
30657         this.files = [];
30658         
30659         this.refresh();
30660     },
30661     
30662     onClick : function(e, el, o)
30663     {
30664         e.preventDefault();
30665         
30666         this.fireEvent('click', this, o);
30667         
30668     },
30669     
30670     closable : function(closable)
30671     {
30672         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30673             
30674             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30675             
30676             if(closable){
30677                 el.show();
30678                 return;
30679             }
30680             
30681             el.hide();
30682             
30683         }, this);
30684     },
30685     
30686     xhrOnLoad : function(xhr)
30687     {
30688         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30689             el.remove();
30690         }, this);
30691         
30692         if (xhr.readyState !== 4) {
30693             this.arrange();
30694             this.fireEvent('exception', this, xhr);
30695             return;
30696         }
30697
30698         var response = Roo.decode(xhr.responseText);
30699         
30700         if(!response.success){
30701             this.arrange();
30702             this.fireEvent('exception', this, xhr);
30703             return;
30704         }
30705         
30706         var file = this.renderPreview(response.data);
30707         
30708         this.files.push(file);
30709         
30710         this.arrange();
30711         
30712         this.fireEvent('afterupload', this, xhr);
30713         
30714     },
30715     
30716     xhrOnError : function(xhr)
30717     {
30718         Roo.log('xhr on error');
30719         
30720         var response = Roo.decode(xhr.responseText);
30721           
30722         Roo.log(response);
30723         
30724         this.arrange();
30725     },
30726     
30727     process : function(file)
30728     {
30729         if(this.fireEvent('process', this, file) !== false){
30730             if(this.editable && file.type.indexOf('image') != -1){
30731                 this.fireEvent('edit', this, file);
30732                 return;
30733             }
30734
30735             this.uploadStart(file, false);
30736
30737             return;
30738         }
30739         
30740     },
30741     
30742     uploadStart : function(file, crop)
30743     {
30744         this.xhr = new XMLHttpRequest();
30745         
30746         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30747             this.arrange();
30748             return;
30749         }
30750         
30751         file.xhr = this.xhr;
30752             
30753         this.managerEl.createChild({
30754             tag : 'div',
30755             cls : 'roo-document-manager-loading',
30756             cn : [
30757                 {
30758                     tag : 'div',
30759                     tooltip : file.name,
30760                     cls : 'roo-document-manager-thumb',
30761                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30762                 }
30763             ]
30764
30765         });
30766
30767         this.xhr.open(this.method, this.url, true);
30768         
30769         var headers = {
30770             "Accept": "application/json",
30771             "Cache-Control": "no-cache",
30772             "X-Requested-With": "XMLHttpRequest"
30773         };
30774         
30775         for (var headerName in headers) {
30776             var headerValue = headers[headerName];
30777             if (headerValue) {
30778                 this.xhr.setRequestHeader(headerName, headerValue);
30779             }
30780         }
30781         
30782         var _this = this;
30783         
30784         this.xhr.onload = function()
30785         {
30786             _this.xhrOnLoad(_this.xhr);
30787         }
30788         
30789         this.xhr.onerror = function()
30790         {
30791             _this.xhrOnError(_this.xhr);
30792         }
30793         
30794         var formData = new FormData();
30795
30796         formData.append('returnHTML', 'NO');
30797         
30798         if(crop){
30799             formData.append('crop', crop);
30800         }
30801         
30802         formData.append(this.paramName, file, file.name);
30803         
30804         var options = {
30805             file : file, 
30806             manually : false
30807         };
30808         
30809         if(this.fireEvent('prepare', this, formData, options) != false){
30810             
30811             if(options.manually){
30812                 return;
30813             }
30814             
30815             this.xhr.send(formData);
30816             return;
30817         };
30818         
30819         this.uploadCancel();
30820     },
30821     
30822     uploadCancel : function()
30823     {
30824         if (this.xhr) {
30825             this.xhr.abort();
30826         }
30827         
30828         this.delegates = [];
30829         
30830         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30831             el.remove();
30832         }, this);
30833         
30834         this.arrange();
30835     },
30836     
30837     renderPreview : function(file)
30838     {
30839         if(typeof(file.target) != 'undefined' && file.target){
30840             return file;
30841         }
30842         
30843         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30844         
30845         var previewEl = this.managerEl.createChild({
30846             tag : 'div',
30847             cls : 'roo-document-manager-preview',
30848             cn : [
30849                 {
30850                     tag : 'div',
30851                     tooltip : file[this.toolTipName],
30852                     cls : 'roo-document-manager-thumb',
30853                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30854                 },
30855                 {
30856                     tag : 'button',
30857                     cls : 'close',
30858                     html : '<i class="fa fa-times-circle"></i>'
30859                 }
30860             ]
30861         });
30862
30863         var close = previewEl.select('button.close', true).first();
30864
30865         close.on('click', this.onRemove, this, file);
30866
30867         file.target = previewEl;
30868
30869         var image = previewEl.select('img', true).first();
30870         
30871         var _this = this;
30872         
30873         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30874         
30875         image.on('click', this.onClick, this, file);
30876         
30877         this.fireEvent('previewrendered', this, file);
30878         
30879         return file;
30880         
30881     },
30882     
30883     onPreviewLoad : function(file, image)
30884     {
30885         if(typeof(file.target) == 'undefined' || !file.target){
30886             return;
30887         }
30888         
30889         var width = image.dom.naturalWidth || image.dom.width;
30890         var height = image.dom.naturalHeight || image.dom.height;
30891         
30892         if(!this.previewResize) {
30893             return;
30894         }
30895         
30896         if(width > height){
30897             file.target.addClass('wide');
30898             return;
30899         }
30900         
30901         file.target.addClass('tall');
30902         return;
30903         
30904     },
30905     
30906     uploadFromSource : function(file, crop)
30907     {
30908         this.xhr = new XMLHttpRequest();
30909         
30910         this.managerEl.createChild({
30911             tag : 'div',
30912             cls : 'roo-document-manager-loading',
30913             cn : [
30914                 {
30915                     tag : 'div',
30916                     tooltip : file.name,
30917                     cls : 'roo-document-manager-thumb',
30918                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30919                 }
30920             ]
30921
30922         });
30923
30924         this.xhr.open(this.method, this.url, true);
30925         
30926         var headers = {
30927             "Accept": "application/json",
30928             "Cache-Control": "no-cache",
30929             "X-Requested-With": "XMLHttpRequest"
30930         };
30931         
30932         for (var headerName in headers) {
30933             var headerValue = headers[headerName];
30934             if (headerValue) {
30935                 this.xhr.setRequestHeader(headerName, headerValue);
30936             }
30937         }
30938         
30939         var _this = this;
30940         
30941         this.xhr.onload = function()
30942         {
30943             _this.xhrOnLoad(_this.xhr);
30944         }
30945         
30946         this.xhr.onerror = function()
30947         {
30948             _this.xhrOnError(_this.xhr);
30949         }
30950         
30951         var formData = new FormData();
30952
30953         formData.append('returnHTML', 'NO');
30954         
30955         formData.append('crop', crop);
30956         
30957         if(typeof(file.filename) != 'undefined'){
30958             formData.append('filename', file.filename);
30959         }
30960         
30961         if(typeof(file.mimetype) != 'undefined'){
30962             formData.append('mimetype', file.mimetype);
30963         }
30964         
30965         Roo.log(formData);
30966         
30967         if(this.fireEvent('prepare', this, formData) != false){
30968             this.xhr.send(formData);
30969         };
30970     }
30971 });
30972
30973 /*
30974 * Licence: LGPL
30975 */
30976
30977 /**
30978  * @class Roo.bootstrap.DocumentViewer
30979  * @extends Roo.bootstrap.Component
30980  * Bootstrap DocumentViewer class
30981  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30982  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30983  * 
30984  * @constructor
30985  * Create a new DocumentViewer
30986  * @param {Object} config The config object
30987  */
30988
30989 Roo.bootstrap.DocumentViewer = function(config){
30990     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30991     
30992     this.addEvents({
30993         /**
30994          * @event initial
30995          * Fire after initEvent
30996          * @param {Roo.bootstrap.DocumentViewer} this
30997          */
30998         "initial" : true,
30999         /**
31000          * @event click
31001          * Fire after click
31002          * @param {Roo.bootstrap.DocumentViewer} this
31003          */
31004         "click" : true,
31005         /**
31006          * @event download
31007          * Fire after download button
31008          * @param {Roo.bootstrap.DocumentViewer} this
31009          */
31010         "download" : true,
31011         /**
31012          * @event trash
31013          * Fire after trash button
31014          * @param {Roo.bootstrap.DocumentViewer} this
31015          */
31016         "trash" : true
31017         
31018     });
31019 };
31020
31021 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31022     
31023     showDownload : true,
31024     
31025     showTrash : true,
31026     
31027     getAutoCreate : function()
31028     {
31029         var cfg = {
31030             tag : 'div',
31031             cls : 'roo-document-viewer',
31032             cn : [
31033                 {
31034                     tag : 'div',
31035                     cls : 'roo-document-viewer-body',
31036                     cn : [
31037                         {
31038                             tag : 'div',
31039                             cls : 'roo-document-viewer-thumb',
31040                             cn : [
31041                                 {
31042                                     tag : 'img',
31043                                     cls : 'roo-document-viewer-image'
31044                                 }
31045                             ]
31046                         }
31047                     ]
31048                 },
31049                 {
31050                     tag : 'div',
31051                     cls : 'roo-document-viewer-footer',
31052                     cn : {
31053                         tag : 'div',
31054                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31055                         cn : [
31056                             {
31057                                 tag : 'div',
31058                                 cls : 'btn-group roo-document-viewer-download',
31059                                 cn : [
31060                                     {
31061                                         tag : 'button',
31062                                         cls : 'btn btn-default',
31063                                         html : '<i class="fa fa-download"></i>'
31064                                     }
31065                                 ]
31066                             },
31067                             {
31068                                 tag : 'div',
31069                                 cls : 'btn-group roo-document-viewer-trash',
31070                                 cn : [
31071                                     {
31072                                         tag : 'button',
31073                                         cls : 'btn btn-default',
31074                                         html : '<i class="fa fa-trash"></i>'
31075                                     }
31076                                 ]
31077                             }
31078                         ]
31079                     }
31080                 }
31081             ]
31082         };
31083         
31084         return cfg;
31085     },
31086     
31087     initEvents : function()
31088     {
31089         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31090         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31091         
31092         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31093         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31094         
31095         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31096         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31097         
31098         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31099         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31100         
31101         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31102         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31103         
31104         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31105         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31106         
31107         this.bodyEl.on('click', this.onClick, this);
31108         this.downloadBtn.on('click', this.onDownload, this);
31109         this.trashBtn.on('click', this.onTrash, this);
31110         
31111         this.downloadBtn.hide();
31112         this.trashBtn.hide();
31113         
31114         if(this.showDownload){
31115             this.downloadBtn.show();
31116         }
31117         
31118         if(this.showTrash){
31119             this.trashBtn.show();
31120         }
31121         
31122         if(!this.showDownload && !this.showTrash) {
31123             this.footerEl.hide();
31124         }
31125         
31126     },
31127     
31128     initial : function()
31129     {
31130         this.fireEvent('initial', this);
31131         
31132     },
31133     
31134     onClick : function(e)
31135     {
31136         e.preventDefault();
31137         
31138         this.fireEvent('click', this);
31139     },
31140     
31141     onDownload : function(e)
31142     {
31143         e.preventDefault();
31144         
31145         this.fireEvent('download', this);
31146     },
31147     
31148     onTrash : function(e)
31149     {
31150         e.preventDefault();
31151         
31152         this.fireEvent('trash', this);
31153     }
31154     
31155 });
31156 /*
31157  * - LGPL
31158  *
31159  * nav progress bar
31160  * 
31161  */
31162
31163 /**
31164  * @class Roo.bootstrap.NavProgressBar
31165  * @extends Roo.bootstrap.Component
31166  * Bootstrap NavProgressBar class
31167  * 
31168  * @constructor
31169  * Create a new nav progress bar
31170  * @param {Object} config The config object
31171  */
31172
31173 Roo.bootstrap.NavProgressBar = function(config){
31174     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31175
31176     this.bullets = this.bullets || [];
31177    
31178 //    Roo.bootstrap.NavProgressBar.register(this);
31179      this.addEvents({
31180         /**
31181              * @event changed
31182              * Fires when the active item changes
31183              * @param {Roo.bootstrap.NavProgressBar} this
31184              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31185              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31186          */
31187         'changed': true
31188      });
31189     
31190 };
31191
31192 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31193     
31194     bullets : [],
31195     barItems : [],
31196     
31197     getAutoCreate : function()
31198     {
31199         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31200         
31201         cfg = {
31202             tag : 'div',
31203             cls : 'roo-navigation-bar-group',
31204             cn : [
31205                 {
31206                     tag : 'div',
31207                     cls : 'roo-navigation-top-bar'
31208                 },
31209                 {
31210                     tag : 'div',
31211                     cls : 'roo-navigation-bullets-bar',
31212                     cn : [
31213                         {
31214                             tag : 'ul',
31215                             cls : 'roo-navigation-bar'
31216                         }
31217                     ]
31218                 },
31219                 
31220                 {
31221                     tag : 'div',
31222                     cls : 'roo-navigation-bottom-bar'
31223                 }
31224             ]
31225             
31226         };
31227         
31228         return cfg;
31229         
31230     },
31231     
31232     initEvents: function() 
31233     {
31234         
31235     },
31236     
31237     onRender : function(ct, position) 
31238     {
31239         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31240         
31241         if(this.bullets.length){
31242             Roo.each(this.bullets, function(b){
31243                this.addItem(b);
31244             }, this);
31245         }
31246         
31247         this.format();
31248         
31249     },
31250     
31251     addItem : function(cfg)
31252     {
31253         var item = new Roo.bootstrap.NavProgressItem(cfg);
31254         
31255         item.parentId = this.id;
31256         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31257         
31258         if(cfg.html){
31259             var top = new Roo.bootstrap.Element({
31260                 tag : 'div',
31261                 cls : 'roo-navigation-bar-text'
31262             });
31263             
31264             var bottom = new Roo.bootstrap.Element({
31265                 tag : 'div',
31266                 cls : 'roo-navigation-bar-text'
31267             });
31268             
31269             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31270             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31271             
31272             var topText = new Roo.bootstrap.Element({
31273                 tag : 'span',
31274                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31275             });
31276             
31277             var bottomText = new Roo.bootstrap.Element({
31278                 tag : 'span',
31279                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31280             });
31281             
31282             topText.onRender(top.el, null);
31283             bottomText.onRender(bottom.el, null);
31284             
31285             item.topEl = top;
31286             item.bottomEl = bottom;
31287         }
31288         
31289         this.barItems.push(item);
31290         
31291         return item;
31292     },
31293     
31294     getActive : function()
31295     {
31296         var active = false;
31297         
31298         Roo.each(this.barItems, function(v){
31299             
31300             if (!v.isActive()) {
31301                 return;
31302             }
31303             
31304             active = v;
31305             return false;
31306             
31307         });
31308         
31309         return active;
31310     },
31311     
31312     setActiveItem : function(item)
31313     {
31314         var prev = false;
31315         
31316         Roo.each(this.barItems, function(v){
31317             if (v.rid == item.rid) {
31318                 return ;
31319             }
31320             
31321             if (v.isActive()) {
31322                 v.setActive(false);
31323                 prev = v;
31324             }
31325         });
31326
31327         item.setActive(true);
31328         
31329         this.fireEvent('changed', this, item, prev);
31330     },
31331     
31332     getBarItem: function(rid)
31333     {
31334         var ret = false;
31335         
31336         Roo.each(this.barItems, function(e) {
31337             if (e.rid != rid) {
31338                 return;
31339             }
31340             
31341             ret =  e;
31342             return false;
31343         });
31344         
31345         return ret;
31346     },
31347     
31348     indexOfItem : function(item)
31349     {
31350         var index = false;
31351         
31352         Roo.each(this.barItems, function(v, i){
31353             
31354             if (v.rid != item.rid) {
31355                 return;
31356             }
31357             
31358             index = i;
31359             return false
31360         });
31361         
31362         return index;
31363     },
31364     
31365     setActiveNext : function()
31366     {
31367         var i = this.indexOfItem(this.getActive());
31368         
31369         if (i > this.barItems.length) {
31370             return;
31371         }
31372         
31373         this.setActiveItem(this.barItems[i+1]);
31374     },
31375     
31376     setActivePrev : function()
31377     {
31378         var i = this.indexOfItem(this.getActive());
31379         
31380         if (i  < 1) {
31381             return;
31382         }
31383         
31384         this.setActiveItem(this.barItems[i-1]);
31385     },
31386     
31387     format : function()
31388     {
31389         if(!this.barItems.length){
31390             return;
31391         }
31392      
31393         var width = 100 / this.barItems.length;
31394         
31395         Roo.each(this.barItems, function(i){
31396             i.el.setStyle('width', width + '%');
31397             i.topEl.el.setStyle('width', width + '%');
31398             i.bottomEl.el.setStyle('width', width + '%');
31399         }, this);
31400         
31401     }
31402     
31403 });
31404 /*
31405  * - LGPL
31406  *
31407  * Nav Progress Item
31408  * 
31409  */
31410
31411 /**
31412  * @class Roo.bootstrap.NavProgressItem
31413  * @extends Roo.bootstrap.Component
31414  * Bootstrap NavProgressItem class
31415  * @cfg {String} rid the reference id
31416  * @cfg {Boolean} active (true|false) Is item active default false
31417  * @cfg {Boolean} disabled (true|false) Is item active default false
31418  * @cfg {String} html
31419  * @cfg {String} position (top|bottom) text position default bottom
31420  * @cfg {String} icon show icon instead of number
31421  * 
31422  * @constructor
31423  * Create a new NavProgressItem
31424  * @param {Object} config The config object
31425  */
31426 Roo.bootstrap.NavProgressItem = function(config){
31427     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31428     this.addEvents({
31429         // raw events
31430         /**
31431          * @event click
31432          * The raw click event for the entire grid.
31433          * @param {Roo.bootstrap.NavProgressItem} this
31434          * @param {Roo.EventObject} e
31435          */
31436         "click" : true
31437     });
31438    
31439 };
31440
31441 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31442     
31443     rid : '',
31444     active : false,
31445     disabled : false,
31446     html : '',
31447     position : 'bottom',
31448     icon : false,
31449     
31450     getAutoCreate : function()
31451     {
31452         var iconCls = 'roo-navigation-bar-item-icon';
31453         
31454         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31455         
31456         var cfg = {
31457             tag: 'li',
31458             cls: 'roo-navigation-bar-item',
31459             cn : [
31460                 {
31461                     tag : 'i',
31462                     cls : iconCls
31463                 }
31464             ]
31465         };
31466         
31467         if(this.active){
31468             cfg.cls += ' active';
31469         }
31470         if(this.disabled){
31471             cfg.cls += ' disabled';
31472         }
31473         
31474         return cfg;
31475     },
31476     
31477     disable : function()
31478     {
31479         this.setDisabled(true);
31480     },
31481     
31482     enable : function()
31483     {
31484         this.setDisabled(false);
31485     },
31486     
31487     initEvents: function() 
31488     {
31489         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31490         
31491         this.iconEl.on('click', this.onClick, this);
31492     },
31493     
31494     onClick : function(e)
31495     {
31496         e.preventDefault();
31497         
31498         if(this.disabled){
31499             return;
31500         }
31501         
31502         if(this.fireEvent('click', this, e) === false){
31503             return;
31504         };
31505         
31506         this.parent().setActiveItem(this);
31507     },
31508     
31509     isActive: function () 
31510     {
31511         return this.active;
31512     },
31513     
31514     setActive : function(state)
31515     {
31516         if(this.active == state){
31517             return;
31518         }
31519         
31520         this.active = state;
31521         
31522         if (state) {
31523             this.el.addClass('active');
31524             return;
31525         }
31526         
31527         this.el.removeClass('active');
31528         
31529         return;
31530     },
31531     
31532     setDisabled : function(state)
31533     {
31534         if(this.disabled == state){
31535             return;
31536         }
31537         
31538         this.disabled = state;
31539         
31540         if (state) {
31541             this.el.addClass('disabled');
31542             return;
31543         }
31544         
31545         this.el.removeClass('disabled');
31546     },
31547     
31548     tooltipEl : function()
31549     {
31550         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31551     }
31552 });
31553  
31554
31555  /*
31556  * - LGPL
31557  *
31558  * FieldLabel
31559  * 
31560  */
31561
31562 /**
31563  * @class Roo.bootstrap.FieldLabel
31564  * @extends Roo.bootstrap.Component
31565  * Bootstrap FieldLabel class
31566  * @cfg {String} html contents of the element
31567  * @cfg {String} tag tag of the element default label
31568  * @cfg {String} cls class of the element
31569  * @cfg {String} target label target 
31570  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31571  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31572  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31573  * @cfg {String} iconTooltip default "This field is required"
31574  * @cfg {String} indicatorpos (left|right) default left
31575  * 
31576  * @constructor
31577  * Create a new FieldLabel
31578  * @param {Object} config The config object
31579  */
31580
31581 Roo.bootstrap.FieldLabel = function(config){
31582     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31583     
31584     this.addEvents({
31585             /**
31586              * @event invalid
31587              * Fires after the field has been marked as invalid.
31588              * @param {Roo.form.FieldLabel} this
31589              * @param {String} msg The validation message
31590              */
31591             invalid : true,
31592             /**
31593              * @event valid
31594              * Fires after the field has been validated with no errors.
31595              * @param {Roo.form.FieldLabel} this
31596              */
31597             valid : true
31598         });
31599 };
31600
31601 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31602     
31603     tag: 'label',
31604     cls: '',
31605     html: '',
31606     target: '',
31607     allowBlank : true,
31608     invalidClass : 'has-warning',
31609     validClass : 'has-success',
31610     iconTooltip : 'This field is required',
31611     indicatorpos : 'left',
31612     
31613     getAutoCreate : function(){
31614         
31615         var cls = "";
31616         if (!this.allowBlank) {
31617             cls  = "visible";
31618         }
31619         
31620         var cfg = {
31621             tag : this.tag,
31622             cls : 'roo-bootstrap-field-label ' + this.cls,
31623             for : this.target,
31624             cn : [
31625                 {
31626                     tag : 'i',
31627                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31628                     tooltip : this.iconTooltip
31629                 },
31630                 {
31631                     tag : 'span',
31632                     html : this.html
31633                 }
31634             ] 
31635         };
31636         
31637         if(this.indicatorpos == 'right'){
31638             var cfg = {
31639                 tag : this.tag,
31640                 cls : 'roo-bootstrap-field-label ' + this.cls,
31641                 for : this.target,
31642                 cn : [
31643                     {
31644                         tag : 'span',
31645                         html : this.html
31646                     },
31647                     {
31648                         tag : 'i',
31649                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31650                         tooltip : this.iconTooltip
31651                     }
31652                 ] 
31653             };
31654         }
31655         
31656         return cfg;
31657     },
31658     
31659     initEvents: function() 
31660     {
31661         Roo.bootstrap.Element.superclass.initEvents.call(this);
31662         
31663         this.indicator = this.indicatorEl();
31664         
31665         if(this.indicator){
31666             this.indicator.removeClass('visible');
31667             this.indicator.addClass('invisible');
31668         }
31669         
31670         Roo.bootstrap.FieldLabel.register(this);
31671     },
31672     
31673     indicatorEl : function()
31674     {
31675         var indicator = this.el.select('i.roo-required-indicator',true).first();
31676         
31677         if(!indicator){
31678             return false;
31679         }
31680         
31681         return indicator;
31682         
31683     },
31684     
31685     /**
31686      * Mark this field as valid
31687      */
31688     markValid : function()
31689     {
31690         if(this.indicator){
31691             this.indicator.removeClass('visible');
31692             this.indicator.addClass('invisible');
31693         }
31694         if (Roo.bootstrap.version == 3) {
31695             this.el.removeClass(this.invalidClass);
31696             this.el.addClass(this.validClass);
31697         } else {
31698             this.el.removeClass('is-invalid');
31699             this.el.addClass('is-valid');
31700         }
31701         
31702         
31703         this.fireEvent('valid', this);
31704     },
31705     
31706     /**
31707      * Mark this field as invalid
31708      * @param {String} msg The validation message
31709      */
31710     markInvalid : function(msg)
31711     {
31712         if(this.indicator){
31713             this.indicator.removeClass('invisible');
31714             this.indicator.addClass('visible');
31715         }
31716           if (Roo.bootstrap.version == 3) {
31717             this.el.removeClass(this.validClass);
31718             this.el.addClass(this.invalidClass);
31719         } else {
31720             this.el.removeClass('is-valid');
31721             this.el.addClass('is-invalid');
31722         }
31723         
31724         
31725         this.fireEvent('invalid', this, msg);
31726     }
31727     
31728    
31729 });
31730
31731 Roo.apply(Roo.bootstrap.FieldLabel, {
31732     
31733     groups: {},
31734     
31735      /**
31736     * register a FieldLabel Group
31737     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31738     */
31739     register : function(label)
31740     {
31741         if(this.groups.hasOwnProperty(label.target)){
31742             return;
31743         }
31744      
31745         this.groups[label.target] = label;
31746         
31747     },
31748     /**
31749     * fetch a FieldLabel Group based on the target
31750     * @param {string} target
31751     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31752     */
31753     get: function(target) {
31754         if (typeof(this.groups[target]) == 'undefined') {
31755             return false;
31756         }
31757         
31758         return this.groups[target] ;
31759     }
31760 });
31761
31762  
31763
31764  /*
31765  * - LGPL
31766  *
31767  * page DateSplitField.
31768  * 
31769  */
31770
31771
31772 /**
31773  * @class Roo.bootstrap.DateSplitField
31774  * @extends Roo.bootstrap.Component
31775  * Bootstrap DateSplitField class
31776  * @cfg {string} fieldLabel - the label associated
31777  * @cfg {Number} labelWidth set the width of label (0-12)
31778  * @cfg {String} labelAlign (top|left)
31779  * @cfg {Boolean} dayAllowBlank (true|false) default false
31780  * @cfg {Boolean} monthAllowBlank (true|false) default false
31781  * @cfg {Boolean} yearAllowBlank (true|false) default false
31782  * @cfg {string} dayPlaceholder 
31783  * @cfg {string} monthPlaceholder
31784  * @cfg {string} yearPlaceholder
31785  * @cfg {string} dayFormat default 'd'
31786  * @cfg {string} monthFormat default 'm'
31787  * @cfg {string} yearFormat default 'Y'
31788  * @cfg {Number} labellg set the width of label (1-12)
31789  * @cfg {Number} labelmd set the width of label (1-12)
31790  * @cfg {Number} labelsm set the width of label (1-12)
31791  * @cfg {Number} labelxs set the width of label (1-12)
31792
31793  *     
31794  * @constructor
31795  * Create a new DateSplitField
31796  * @param {Object} config The config object
31797  */
31798
31799 Roo.bootstrap.DateSplitField = function(config){
31800     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31801     
31802     this.addEvents({
31803         // raw events
31804          /**
31805          * @event years
31806          * getting the data of years
31807          * @param {Roo.bootstrap.DateSplitField} this
31808          * @param {Object} years
31809          */
31810         "years" : true,
31811         /**
31812          * @event days
31813          * getting the data of days
31814          * @param {Roo.bootstrap.DateSplitField} this
31815          * @param {Object} days
31816          */
31817         "days" : true,
31818         /**
31819          * @event invalid
31820          * Fires after the field has been marked as invalid.
31821          * @param {Roo.form.Field} this
31822          * @param {String} msg The validation message
31823          */
31824         invalid : true,
31825        /**
31826          * @event valid
31827          * Fires after the field has been validated with no errors.
31828          * @param {Roo.form.Field} this
31829          */
31830         valid : true
31831     });
31832 };
31833
31834 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31835     
31836     fieldLabel : '',
31837     labelAlign : 'top',
31838     labelWidth : 3,
31839     dayAllowBlank : false,
31840     monthAllowBlank : false,
31841     yearAllowBlank : false,
31842     dayPlaceholder : '',
31843     monthPlaceholder : '',
31844     yearPlaceholder : '',
31845     dayFormat : 'd',
31846     monthFormat : 'm',
31847     yearFormat : 'Y',
31848     isFormField : true,
31849     labellg : 0,
31850     labelmd : 0,
31851     labelsm : 0,
31852     labelxs : 0,
31853     
31854     getAutoCreate : function()
31855     {
31856         var cfg = {
31857             tag : 'div',
31858             cls : 'row roo-date-split-field-group',
31859             cn : [
31860                 {
31861                     tag : 'input',
31862                     type : 'hidden',
31863                     cls : 'form-hidden-field roo-date-split-field-group-value',
31864                     name : this.name
31865                 }
31866             ]
31867         };
31868         
31869         var labelCls = 'col-md-12';
31870         var contentCls = 'col-md-4';
31871         
31872         if(this.fieldLabel){
31873             
31874             var label = {
31875                 tag : 'div',
31876                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31877                 cn : [
31878                     {
31879                         tag : 'label',
31880                         html : this.fieldLabel
31881                     }
31882                 ]
31883             };
31884             
31885             if(this.labelAlign == 'left'){
31886             
31887                 if(this.labelWidth > 12){
31888                     label.style = "width: " + this.labelWidth + 'px';
31889                 }
31890
31891                 if(this.labelWidth < 13 && this.labelmd == 0){
31892                     this.labelmd = this.labelWidth;
31893                 }
31894
31895                 if(this.labellg > 0){
31896                     labelCls = ' col-lg-' + this.labellg;
31897                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31898                 }
31899
31900                 if(this.labelmd > 0){
31901                     labelCls = ' col-md-' + this.labelmd;
31902                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31903                 }
31904
31905                 if(this.labelsm > 0){
31906                     labelCls = ' col-sm-' + this.labelsm;
31907                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31908                 }
31909
31910                 if(this.labelxs > 0){
31911                     labelCls = ' col-xs-' + this.labelxs;
31912                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31913                 }
31914             }
31915             
31916             label.cls += ' ' + labelCls;
31917             
31918             cfg.cn.push(label);
31919         }
31920         
31921         Roo.each(['day', 'month', 'year'], function(t){
31922             cfg.cn.push({
31923                 tag : 'div',
31924                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31925             });
31926         }, this);
31927         
31928         return cfg;
31929     },
31930     
31931     inputEl: function ()
31932     {
31933         return this.el.select('.roo-date-split-field-group-value', true).first();
31934     },
31935     
31936     onRender : function(ct, position) 
31937     {
31938         var _this = this;
31939         
31940         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31941         
31942         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31943         
31944         this.dayField = new Roo.bootstrap.ComboBox({
31945             allowBlank : this.dayAllowBlank,
31946             alwaysQuery : true,
31947             displayField : 'value',
31948             editable : false,
31949             fieldLabel : '',
31950             forceSelection : true,
31951             mode : 'local',
31952             placeholder : this.dayPlaceholder,
31953             selectOnFocus : true,
31954             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31955             triggerAction : 'all',
31956             typeAhead : true,
31957             valueField : 'value',
31958             store : new Roo.data.SimpleStore({
31959                 data : (function() {    
31960                     var days = [];
31961                     _this.fireEvent('days', _this, days);
31962                     return days;
31963                 })(),
31964                 fields : [ 'value' ]
31965             }),
31966             listeners : {
31967                 select : function (_self, record, index)
31968                 {
31969                     _this.setValue(_this.getValue());
31970                 }
31971             }
31972         });
31973
31974         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31975         
31976         this.monthField = new Roo.bootstrap.MonthField({
31977             after : '<i class=\"fa fa-calendar\"></i>',
31978             allowBlank : this.monthAllowBlank,
31979             placeholder : this.monthPlaceholder,
31980             readOnly : true,
31981             listeners : {
31982                 render : function (_self)
31983                 {
31984                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31985                         e.preventDefault();
31986                         _self.focus();
31987                     });
31988                 },
31989                 select : function (_self, oldvalue, newvalue)
31990                 {
31991                     _this.setValue(_this.getValue());
31992                 }
31993             }
31994         });
31995         
31996         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31997         
31998         this.yearField = new Roo.bootstrap.ComboBox({
31999             allowBlank : this.yearAllowBlank,
32000             alwaysQuery : true,
32001             displayField : 'value',
32002             editable : false,
32003             fieldLabel : '',
32004             forceSelection : true,
32005             mode : 'local',
32006             placeholder : this.yearPlaceholder,
32007             selectOnFocus : true,
32008             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32009             triggerAction : 'all',
32010             typeAhead : true,
32011             valueField : 'value',
32012             store : new Roo.data.SimpleStore({
32013                 data : (function() {
32014                     var years = [];
32015                     _this.fireEvent('years', _this, years);
32016                     return years;
32017                 })(),
32018                 fields : [ 'value' ]
32019             }),
32020             listeners : {
32021                 select : function (_self, record, index)
32022                 {
32023                     _this.setValue(_this.getValue());
32024                 }
32025             }
32026         });
32027
32028         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32029     },
32030     
32031     setValue : function(v, format)
32032     {
32033         this.inputEl.dom.value = v;
32034         
32035         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32036         
32037         var d = Date.parseDate(v, f);
32038         
32039         if(!d){
32040             this.validate();
32041             return;
32042         }
32043         
32044         this.setDay(d.format(this.dayFormat));
32045         this.setMonth(d.format(this.monthFormat));
32046         this.setYear(d.format(this.yearFormat));
32047         
32048         this.validate();
32049         
32050         return;
32051     },
32052     
32053     setDay : function(v)
32054     {
32055         this.dayField.setValue(v);
32056         this.inputEl.dom.value = this.getValue();
32057         this.validate();
32058         return;
32059     },
32060     
32061     setMonth : function(v)
32062     {
32063         this.monthField.setValue(v, true);
32064         this.inputEl.dom.value = this.getValue();
32065         this.validate();
32066         return;
32067     },
32068     
32069     setYear : function(v)
32070     {
32071         this.yearField.setValue(v);
32072         this.inputEl.dom.value = this.getValue();
32073         this.validate();
32074         return;
32075     },
32076     
32077     getDay : function()
32078     {
32079         return this.dayField.getValue();
32080     },
32081     
32082     getMonth : function()
32083     {
32084         return this.monthField.getValue();
32085     },
32086     
32087     getYear : function()
32088     {
32089         return this.yearField.getValue();
32090     },
32091     
32092     getValue : function()
32093     {
32094         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32095         
32096         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32097         
32098         return date;
32099     },
32100     
32101     reset : function()
32102     {
32103         this.setDay('');
32104         this.setMonth('');
32105         this.setYear('');
32106         this.inputEl.dom.value = '';
32107         this.validate();
32108         return;
32109     },
32110     
32111     validate : function()
32112     {
32113         var d = this.dayField.validate();
32114         var m = this.monthField.validate();
32115         var y = this.yearField.validate();
32116         
32117         var valid = true;
32118         
32119         if(
32120                 (!this.dayAllowBlank && !d) ||
32121                 (!this.monthAllowBlank && !m) ||
32122                 (!this.yearAllowBlank && !y)
32123         ){
32124             valid = false;
32125         }
32126         
32127         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32128             return valid;
32129         }
32130         
32131         if(valid){
32132             this.markValid();
32133             return valid;
32134         }
32135         
32136         this.markInvalid();
32137         
32138         return valid;
32139     },
32140     
32141     markValid : function()
32142     {
32143         
32144         var label = this.el.select('label', true).first();
32145         var icon = this.el.select('i.fa-star', true).first();
32146
32147         if(label && icon){
32148             icon.remove();
32149         }
32150         
32151         this.fireEvent('valid', this);
32152     },
32153     
32154      /**
32155      * Mark this field as invalid
32156      * @param {String} msg The validation message
32157      */
32158     markInvalid : function(msg)
32159     {
32160         
32161         var label = this.el.select('label', true).first();
32162         var icon = this.el.select('i.fa-star', true).first();
32163
32164         if(label && !icon){
32165             this.el.select('.roo-date-split-field-label', true).createChild({
32166                 tag : 'i',
32167                 cls : 'text-danger fa fa-lg fa-star',
32168                 tooltip : 'This field is required',
32169                 style : 'margin-right:5px;'
32170             }, label, true);
32171         }
32172         
32173         this.fireEvent('invalid', this, msg);
32174     },
32175     
32176     clearInvalid : function()
32177     {
32178         var label = this.el.select('label', true).first();
32179         var icon = this.el.select('i.fa-star', true).first();
32180
32181         if(label && icon){
32182             icon.remove();
32183         }
32184         
32185         this.fireEvent('valid', this);
32186     },
32187     
32188     getName: function()
32189     {
32190         return this.name;
32191     }
32192     
32193 });
32194
32195  /**
32196  *
32197  * This is based on 
32198  * http://masonry.desandro.com
32199  *
32200  * The idea is to render all the bricks based on vertical width...
32201  *
32202  * The original code extends 'outlayer' - we might need to use that....
32203  * 
32204  */
32205
32206
32207 /**
32208  * @class Roo.bootstrap.LayoutMasonry
32209  * @extends Roo.bootstrap.Component
32210  * Bootstrap Layout Masonry class
32211  * 
32212  * @constructor
32213  * Create a new Element
32214  * @param {Object} config The config object
32215  */
32216
32217 Roo.bootstrap.LayoutMasonry = function(config){
32218     
32219     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32220     
32221     this.bricks = [];
32222     
32223     Roo.bootstrap.LayoutMasonry.register(this);
32224     
32225     this.addEvents({
32226         // raw events
32227         /**
32228          * @event layout
32229          * Fire after layout the items
32230          * @param {Roo.bootstrap.LayoutMasonry} this
32231          * @param {Roo.EventObject} e
32232          */
32233         "layout" : true
32234     });
32235     
32236 };
32237
32238 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32239     
32240     /**
32241      * @cfg {Boolean} isLayoutInstant = no animation?
32242      */   
32243     isLayoutInstant : false, // needed?
32244    
32245     /**
32246      * @cfg {Number} boxWidth  width of the columns
32247      */   
32248     boxWidth : 450,
32249     
32250       /**
32251      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32252      */   
32253     boxHeight : 0,
32254     
32255     /**
32256      * @cfg {Number} padWidth padding below box..
32257      */   
32258     padWidth : 10, 
32259     
32260     /**
32261      * @cfg {Number} gutter gutter width..
32262      */   
32263     gutter : 10,
32264     
32265      /**
32266      * @cfg {Number} maxCols maximum number of columns
32267      */   
32268     
32269     maxCols: 0,
32270     
32271     /**
32272      * @cfg {Boolean} isAutoInitial defalut true
32273      */   
32274     isAutoInitial : true, 
32275     
32276     containerWidth: 0,
32277     
32278     /**
32279      * @cfg {Boolean} isHorizontal defalut false
32280      */   
32281     isHorizontal : false, 
32282
32283     currentSize : null,
32284     
32285     tag: 'div',
32286     
32287     cls: '',
32288     
32289     bricks: null, //CompositeElement
32290     
32291     cols : 1,
32292     
32293     _isLayoutInited : false,
32294     
32295 //    isAlternative : false, // only use for vertical layout...
32296     
32297     /**
32298      * @cfg {Number} alternativePadWidth padding below box..
32299      */   
32300     alternativePadWidth : 50,
32301     
32302     selectedBrick : [],
32303     
32304     getAutoCreate : function(){
32305         
32306         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32307         
32308         var cfg = {
32309             tag: this.tag,
32310             cls: 'blog-masonary-wrapper ' + this.cls,
32311             cn : {
32312                 cls : 'mas-boxes masonary'
32313             }
32314         };
32315         
32316         return cfg;
32317     },
32318     
32319     getChildContainer: function( )
32320     {
32321         if (this.boxesEl) {
32322             return this.boxesEl;
32323         }
32324         
32325         this.boxesEl = this.el.select('.mas-boxes').first();
32326         
32327         return this.boxesEl;
32328     },
32329     
32330     
32331     initEvents : function()
32332     {
32333         var _this = this;
32334         
32335         if(this.isAutoInitial){
32336             Roo.log('hook children rendered');
32337             this.on('childrenrendered', function() {
32338                 Roo.log('children rendered');
32339                 _this.initial();
32340             } ,this);
32341         }
32342     },
32343     
32344     initial : function()
32345     {
32346         this.selectedBrick = [];
32347         
32348         this.currentSize = this.el.getBox(true);
32349         
32350         Roo.EventManager.onWindowResize(this.resize, this); 
32351
32352         if(!this.isAutoInitial){
32353             this.layout();
32354             return;
32355         }
32356         
32357         this.layout();
32358         
32359         return;
32360         //this.layout.defer(500,this);
32361         
32362     },
32363     
32364     resize : function()
32365     {
32366         var cs = this.el.getBox(true);
32367         
32368         if (
32369                 this.currentSize.width == cs.width && 
32370                 this.currentSize.x == cs.x && 
32371                 this.currentSize.height == cs.height && 
32372                 this.currentSize.y == cs.y 
32373         ) {
32374             Roo.log("no change in with or X or Y");
32375             return;
32376         }
32377         
32378         this.currentSize = cs;
32379         
32380         this.layout();
32381         
32382     },
32383     
32384     layout : function()
32385     {   
32386         this._resetLayout();
32387         
32388         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32389         
32390         this.layoutItems( isInstant );
32391       
32392         this._isLayoutInited = true;
32393         
32394         this.fireEvent('layout', this);
32395         
32396     },
32397     
32398     _resetLayout : function()
32399     {
32400         if(this.isHorizontal){
32401             this.horizontalMeasureColumns();
32402             return;
32403         }
32404         
32405         this.verticalMeasureColumns();
32406         
32407     },
32408     
32409     verticalMeasureColumns : function()
32410     {
32411         this.getContainerWidth();
32412         
32413 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32414 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32415 //            return;
32416 //        }
32417         
32418         var boxWidth = this.boxWidth + this.padWidth;
32419         
32420         if(this.containerWidth < this.boxWidth){
32421             boxWidth = this.containerWidth
32422         }
32423         
32424         var containerWidth = this.containerWidth;
32425         
32426         var cols = Math.floor(containerWidth / boxWidth);
32427         
32428         this.cols = Math.max( cols, 1 );
32429         
32430         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32431         
32432         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32433         
32434         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32435         
32436         this.colWidth = boxWidth + avail - this.padWidth;
32437         
32438         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32439         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32440     },
32441     
32442     horizontalMeasureColumns : function()
32443     {
32444         this.getContainerWidth();
32445         
32446         var boxWidth = this.boxWidth;
32447         
32448         if(this.containerWidth < boxWidth){
32449             boxWidth = this.containerWidth;
32450         }
32451         
32452         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32453         
32454         this.el.setHeight(boxWidth);
32455         
32456     },
32457     
32458     getContainerWidth : function()
32459     {
32460         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32461     },
32462     
32463     layoutItems : function( isInstant )
32464     {
32465         Roo.log(this.bricks);
32466         
32467         var items = Roo.apply([], this.bricks);
32468         
32469         if(this.isHorizontal){
32470             this._horizontalLayoutItems( items , isInstant );
32471             return;
32472         }
32473         
32474 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32475 //            this._verticalAlternativeLayoutItems( items , isInstant );
32476 //            return;
32477 //        }
32478         
32479         this._verticalLayoutItems( items , isInstant );
32480         
32481     },
32482     
32483     _verticalLayoutItems : function ( items , isInstant)
32484     {
32485         if ( !items || !items.length ) {
32486             return;
32487         }
32488         
32489         var standard = [
32490             ['xs', 'xs', 'xs', 'tall'],
32491             ['xs', 'xs', 'tall'],
32492             ['xs', 'xs', 'sm'],
32493             ['xs', 'xs', 'xs'],
32494             ['xs', 'tall'],
32495             ['xs', 'sm'],
32496             ['xs', 'xs'],
32497             ['xs'],
32498             
32499             ['sm', 'xs', 'xs'],
32500             ['sm', 'xs'],
32501             ['sm'],
32502             
32503             ['tall', 'xs', 'xs', 'xs'],
32504             ['tall', 'xs', 'xs'],
32505             ['tall', 'xs'],
32506             ['tall']
32507             
32508         ];
32509         
32510         var queue = [];
32511         
32512         var boxes = [];
32513         
32514         var box = [];
32515         
32516         Roo.each(items, function(item, k){
32517             
32518             switch (item.size) {
32519                 // these layouts take up a full box,
32520                 case 'md' :
32521                 case 'md-left' :
32522                 case 'md-right' :
32523                 case 'wide' :
32524                     
32525                     if(box.length){
32526                         boxes.push(box);
32527                         box = [];
32528                     }
32529                     
32530                     boxes.push([item]);
32531                     
32532                     break;
32533                     
32534                 case 'xs' :
32535                 case 'sm' :
32536                 case 'tall' :
32537                     
32538                     box.push(item);
32539                     
32540                     break;
32541                 default :
32542                     break;
32543                     
32544             }
32545             
32546         }, this);
32547         
32548         if(box.length){
32549             boxes.push(box);
32550             box = [];
32551         }
32552         
32553         var filterPattern = function(box, length)
32554         {
32555             if(!box.length){
32556                 return;
32557             }
32558             
32559             var match = false;
32560             
32561             var pattern = box.slice(0, length);
32562             
32563             var format = [];
32564             
32565             Roo.each(pattern, function(i){
32566                 format.push(i.size);
32567             }, this);
32568             
32569             Roo.each(standard, function(s){
32570                 
32571                 if(String(s) != String(format)){
32572                     return;
32573                 }
32574                 
32575                 match = true;
32576                 return false;
32577                 
32578             }, this);
32579             
32580             if(!match && length == 1){
32581                 return;
32582             }
32583             
32584             if(!match){
32585                 filterPattern(box, length - 1);
32586                 return;
32587             }
32588                 
32589             queue.push(pattern);
32590
32591             box = box.slice(length, box.length);
32592
32593             filterPattern(box, 4);
32594
32595             return;
32596             
32597         }
32598         
32599         Roo.each(boxes, function(box, k){
32600             
32601             if(!box.length){
32602                 return;
32603             }
32604             
32605             if(box.length == 1){
32606                 queue.push(box);
32607                 return;
32608             }
32609             
32610             filterPattern(box, 4);
32611             
32612         }, this);
32613         
32614         this._processVerticalLayoutQueue( queue, isInstant );
32615         
32616     },
32617     
32618 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32619 //    {
32620 //        if ( !items || !items.length ) {
32621 //            return;
32622 //        }
32623 //
32624 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32625 //        
32626 //    },
32627     
32628     _horizontalLayoutItems : function ( items , isInstant)
32629     {
32630         if ( !items || !items.length || items.length < 3) {
32631             return;
32632         }
32633         
32634         items.reverse();
32635         
32636         var eItems = items.slice(0, 3);
32637         
32638         items = items.slice(3, items.length);
32639         
32640         var standard = [
32641             ['xs', 'xs', 'xs', 'wide'],
32642             ['xs', 'xs', 'wide'],
32643             ['xs', 'xs', 'sm'],
32644             ['xs', 'xs', 'xs'],
32645             ['xs', 'wide'],
32646             ['xs', 'sm'],
32647             ['xs', 'xs'],
32648             ['xs'],
32649             
32650             ['sm', 'xs', 'xs'],
32651             ['sm', 'xs'],
32652             ['sm'],
32653             
32654             ['wide', 'xs', 'xs', 'xs'],
32655             ['wide', 'xs', 'xs'],
32656             ['wide', 'xs'],
32657             ['wide'],
32658             
32659             ['wide-thin']
32660         ];
32661         
32662         var queue = [];
32663         
32664         var boxes = [];
32665         
32666         var box = [];
32667         
32668         Roo.each(items, function(item, k){
32669             
32670             switch (item.size) {
32671                 case 'md' :
32672                 case 'md-left' :
32673                 case 'md-right' :
32674                 case 'tall' :
32675                     
32676                     if(box.length){
32677                         boxes.push(box);
32678                         box = [];
32679                     }
32680                     
32681                     boxes.push([item]);
32682                     
32683                     break;
32684                     
32685                 case 'xs' :
32686                 case 'sm' :
32687                 case 'wide' :
32688                 case 'wide-thin' :
32689                     
32690                     box.push(item);
32691                     
32692                     break;
32693                 default :
32694                     break;
32695                     
32696             }
32697             
32698         }, this);
32699         
32700         if(box.length){
32701             boxes.push(box);
32702             box = [];
32703         }
32704         
32705         var filterPattern = function(box, length)
32706         {
32707             if(!box.length){
32708                 return;
32709             }
32710             
32711             var match = false;
32712             
32713             var pattern = box.slice(0, length);
32714             
32715             var format = [];
32716             
32717             Roo.each(pattern, function(i){
32718                 format.push(i.size);
32719             }, this);
32720             
32721             Roo.each(standard, function(s){
32722                 
32723                 if(String(s) != String(format)){
32724                     return;
32725                 }
32726                 
32727                 match = true;
32728                 return false;
32729                 
32730             }, this);
32731             
32732             if(!match && length == 1){
32733                 return;
32734             }
32735             
32736             if(!match){
32737                 filterPattern(box, length - 1);
32738                 return;
32739             }
32740                 
32741             queue.push(pattern);
32742
32743             box = box.slice(length, box.length);
32744
32745             filterPattern(box, 4);
32746
32747             return;
32748             
32749         }
32750         
32751         Roo.each(boxes, function(box, k){
32752             
32753             if(!box.length){
32754                 return;
32755             }
32756             
32757             if(box.length == 1){
32758                 queue.push(box);
32759                 return;
32760             }
32761             
32762             filterPattern(box, 4);
32763             
32764         }, this);
32765         
32766         
32767         var prune = [];
32768         
32769         var pos = this.el.getBox(true);
32770         
32771         var minX = pos.x;
32772         
32773         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32774         
32775         var hit_end = false;
32776         
32777         Roo.each(queue, function(box){
32778             
32779             if(hit_end){
32780                 
32781                 Roo.each(box, function(b){
32782                 
32783                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32784                     b.el.hide();
32785
32786                 }, this);
32787
32788                 return;
32789             }
32790             
32791             var mx = 0;
32792             
32793             Roo.each(box, function(b){
32794                 
32795                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32796                 b.el.show();
32797
32798                 mx = Math.max(mx, b.x);
32799                 
32800             }, this);
32801             
32802             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32803             
32804             if(maxX < minX){
32805                 
32806                 Roo.each(box, function(b){
32807                 
32808                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32809                     b.el.hide();
32810                     
32811                 }, this);
32812                 
32813                 hit_end = true;
32814                 
32815                 return;
32816             }
32817             
32818             prune.push(box);
32819             
32820         }, this);
32821         
32822         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32823     },
32824     
32825     /** Sets position of item in DOM
32826     * @param {Element} item
32827     * @param {Number} x - horizontal position
32828     * @param {Number} y - vertical position
32829     * @param {Boolean} isInstant - disables transitions
32830     */
32831     _processVerticalLayoutQueue : function( queue, isInstant )
32832     {
32833         var pos = this.el.getBox(true);
32834         var x = pos.x;
32835         var y = pos.y;
32836         var maxY = [];
32837         
32838         for (var i = 0; i < this.cols; i++){
32839             maxY[i] = pos.y;
32840         }
32841         
32842         Roo.each(queue, function(box, k){
32843             
32844             var col = k % this.cols;
32845             
32846             Roo.each(box, function(b,kk){
32847                 
32848                 b.el.position('absolute');
32849                 
32850                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32851                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32852                 
32853                 if(b.size == 'md-left' || b.size == 'md-right'){
32854                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32855                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32856                 }
32857                 
32858                 b.el.setWidth(width);
32859                 b.el.setHeight(height);
32860                 // iframe?
32861                 b.el.select('iframe',true).setSize(width,height);
32862                 
32863             }, this);
32864             
32865             for (var i = 0; i < this.cols; i++){
32866                 
32867                 if(maxY[i] < maxY[col]){
32868                     col = i;
32869                     continue;
32870                 }
32871                 
32872                 col = Math.min(col, i);
32873                 
32874             }
32875             
32876             x = pos.x + col * (this.colWidth + this.padWidth);
32877             
32878             y = maxY[col];
32879             
32880             var positions = [];
32881             
32882             switch (box.length){
32883                 case 1 :
32884                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32885                     break;
32886                 case 2 :
32887                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32888                     break;
32889                 case 3 :
32890                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32891                     break;
32892                 case 4 :
32893                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32894                     break;
32895                 default :
32896                     break;
32897             }
32898             
32899             Roo.each(box, function(b,kk){
32900                 
32901                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32902                 
32903                 var sz = b.el.getSize();
32904                 
32905                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32906                 
32907             }, this);
32908             
32909         }, this);
32910         
32911         var mY = 0;
32912         
32913         for (var i = 0; i < this.cols; i++){
32914             mY = Math.max(mY, maxY[i]);
32915         }
32916         
32917         this.el.setHeight(mY - pos.y);
32918         
32919     },
32920     
32921 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32922 //    {
32923 //        var pos = this.el.getBox(true);
32924 //        var x = pos.x;
32925 //        var y = pos.y;
32926 //        var maxX = pos.right;
32927 //        
32928 //        var maxHeight = 0;
32929 //        
32930 //        Roo.each(items, function(item, k){
32931 //            
32932 //            var c = k % 2;
32933 //            
32934 //            item.el.position('absolute');
32935 //                
32936 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32937 //
32938 //            item.el.setWidth(width);
32939 //
32940 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32941 //
32942 //            item.el.setHeight(height);
32943 //            
32944 //            if(c == 0){
32945 //                item.el.setXY([x, y], isInstant ? false : true);
32946 //            } else {
32947 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32948 //            }
32949 //            
32950 //            y = y + height + this.alternativePadWidth;
32951 //            
32952 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32953 //            
32954 //        }, this);
32955 //        
32956 //        this.el.setHeight(maxHeight);
32957 //        
32958 //    },
32959     
32960     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32961     {
32962         var pos = this.el.getBox(true);
32963         
32964         var minX = pos.x;
32965         var minY = pos.y;
32966         
32967         var maxX = pos.right;
32968         
32969         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32970         
32971         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32972         
32973         Roo.each(queue, function(box, k){
32974             
32975             Roo.each(box, function(b, kk){
32976                 
32977                 b.el.position('absolute');
32978                 
32979                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32980                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32981                 
32982                 if(b.size == 'md-left' || b.size == 'md-right'){
32983                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32984                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32985                 }
32986                 
32987                 b.el.setWidth(width);
32988                 b.el.setHeight(height);
32989                 
32990             }, this);
32991             
32992             if(!box.length){
32993                 return;
32994             }
32995             
32996             var positions = [];
32997             
32998             switch (box.length){
32999                 case 1 :
33000                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33001                     break;
33002                 case 2 :
33003                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33004                     break;
33005                 case 3 :
33006                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33007                     break;
33008                 case 4 :
33009                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33010                     break;
33011                 default :
33012                     break;
33013             }
33014             
33015             Roo.each(box, function(b,kk){
33016                 
33017                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33018                 
33019                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33020                 
33021             }, this);
33022             
33023         }, this);
33024         
33025     },
33026     
33027     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33028     {
33029         Roo.each(eItems, function(b,k){
33030             
33031             b.size = (k == 0) ? 'sm' : 'xs';
33032             b.x = (k == 0) ? 2 : 1;
33033             b.y = (k == 0) ? 2 : 1;
33034             
33035             b.el.position('absolute');
33036             
33037             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33038                 
33039             b.el.setWidth(width);
33040             
33041             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33042             
33043             b.el.setHeight(height);
33044             
33045         }, this);
33046
33047         var positions = [];
33048         
33049         positions.push({
33050             x : maxX - this.unitWidth * 2 - this.gutter,
33051             y : minY
33052         });
33053         
33054         positions.push({
33055             x : maxX - this.unitWidth,
33056             y : minY + (this.unitWidth + this.gutter) * 2
33057         });
33058         
33059         positions.push({
33060             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33061             y : minY
33062         });
33063         
33064         Roo.each(eItems, function(b,k){
33065             
33066             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33067
33068         }, this);
33069         
33070     },
33071     
33072     getVerticalOneBoxColPositions : function(x, y, box)
33073     {
33074         var pos = [];
33075         
33076         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33077         
33078         if(box[0].size == 'md-left'){
33079             rand = 0;
33080         }
33081         
33082         if(box[0].size == 'md-right'){
33083             rand = 1;
33084         }
33085         
33086         pos.push({
33087             x : x + (this.unitWidth + this.gutter) * rand,
33088             y : y
33089         });
33090         
33091         return pos;
33092     },
33093     
33094     getVerticalTwoBoxColPositions : function(x, y, box)
33095     {
33096         var pos = [];
33097         
33098         if(box[0].size == 'xs'){
33099             
33100             pos.push({
33101                 x : x,
33102                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33103             });
33104
33105             pos.push({
33106                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33107                 y : y
33108             });
33109             
33110             return pos;
33111             
33112         }
33113         
33114         pos.push({
33115             x : x,
33116             y : y
33117         });
33118
33119         pos.push({
33120             x : x + (this.unitWidth + this.gutter) * 2,
33121             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33122         });
33123         
33124         return pos;
33125         
33126     },
33127     
33128     getVerticalThreeBoxColPositions : function(x, y, box)
33129     {
33130         var pos = [];
33131         
33132         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33133             
33134             pos.push({
33135                 x : x,
33136                 y : y
33137             });
33138
33139             pos.push({
33140                 x : x + (this.unitWidth + this.gutter) * 1,
33141                 y : y
33142             });
33143             
33144             pos.push({
33145                 x : x + (this.unitWidth + this.gutter) * 2,
33146                 y : y
33147             });
33148             
33149             return pos;
33150             
33151         }
33152         
33153         if(box[0].size == 'xs' && box[1].size == 'xs'){
33154             
33155             pos.push({
33156                 x : x,
33157                 y : y
33158             });
33159
33160             pos.push({
33161                 x : x,
33162                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33163             });
33164             
33165             pos.push({
33166                 x : x + (this.unitWidth + this.gutter) * 1,
33167                 y : y
33168             });
33169             
33170             return pos;
33171             
33172         }
33173         
33174         pos.push({
33175             x : x,
33176             y : y
33177         });
33178
33179         pos.push({
33180             x : x + (this.unitWidth + this.gutter) * 2,
33181             y : y
33182         });
33183
33184         pos.push({
33185             x : x + (this.unitWidth + this.gutter) * 2,
33186             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33187         });
33188             
33189         return pos;
33190         
33191     },
33192     
33193     getVerticalFourBoxColPositions : function(x, y, box)
33194     {
33195         var pos = [];
33196         
33197         if(box[0].size == 'xs'){
33198             
33199             pos.push({
33200                 x : x,
33201                 y : y
33202             });
33203
33204             pos.push({
33205                 x : x,
33206                 y : y + (this.unitHeight + this.gutter) * 1
33207             });
33208             
33209             pos.push({
33210                 x : x,
33211                 y : y + (this.unitHeight + this.gutter) * 2
33212             });
33213             
33214             pos.push({
33215                 x : x + (this.unitWidth + this.gutter) * 1,
33216                 y : y
33217             });
33218             
33219             return pos;
33220             
33221         }
33222         
33223         pos.push({
33224             x : x,
33225             y : y
33226         });
33227
33228         pos.push({
33229             x : x + (this.unitWidth + this.gutter) * 2,
33230             y : y
33231         });
33232
33233         pos.push({
33234             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33235             y : y + (this.unitHeight + this.gutter) * 1
33236         });
33237
33238         pos.push({
33239             x : x + (this.unitWidth + this.gutter) * 2,
33240             y : y + (this.unitWidth + this.gutter) * 2
33241         });
33242
33243         return pos;
33244         
33245     },
33246     
33247     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33248     {
33249         var pos = [];
33250         
33251         if(box[0].size == 'md-left'){
33252             pos.push({
33253                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33254                 y : minY
33255             });
33256             
33257             return pos;
33258         }
33259         
33260         if(box[0].size == 'md-right'){
33261             pos.push({
33262                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33263                 y : minY + (this.unitWidth + this.gutter) * 1
33264             });
33265             
33266             return pos;
33267         }
33268         
33269         var rand = Math.floor(Math.random() * (4 - box[0].y));
33270         
33271         pos.push({
33272             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33273             y : minY + (this.unitWidth + this.gutter) * rand
33274         });
33275         
33276         return pos;
33277         
33278     },
33279     
33280     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33281     {
33282         var pos = [];
33283         
33284         if(box[0].size == 'xs'){
33285             
33286             pos.push({
33287                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33288                 y : minY
33289             });
33290
33291             pos.push({
33292                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33293                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33294             });
33295             
33296             return pos;
33297             
33298         }
33299         
33300         pos.push({
33301             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33302             y : minY
33303         });
33304
33305         pos.push({
33306             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33307             y : minY + (this.unitWidth + this.gutter) * 2
33308         });
33309         
33310         return pos;
33311         
33312     },
33313     
33314     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33315     {
33316         var pos = [];
33317         
33318         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33319             
33320             pos.push({
33321                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33322                 y : minY
33323             });
33324
33325             pos.push({
33326                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33327                 y : minY + (this.unitWidth + this.gutter) * 1
33328             });
33329             
33330             pos.push({
33331                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33332                 y : minY + (this.unitWidth + this.gutter) * 2
33333             });
33334             
33335             return pos;
33336             
33337         }
33338         
33339         if(box[0].size == 'xs' && box[1].size == 'xs'){
33340             
33341             pos.push({
33342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33343                 y : minY
33344             });
33345
33346             pos.push({
33347                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33348                 y : minY
33349             });
33350             
33351             pos.push({
33352                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33353                 y : minY + (this.unitWidth + this.gutter) * 1
33354             });
33355             
33356             return pos;
33357             
33358         }
33359         
33360         pos.push({
33361             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33362             y : minY
33363         });
33364
33365         pos.push({
33366             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33367             y : minY + (this.unitWidth + this.gutter) * 2
33368         });
33369
33370         pos.push({
33371             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33372             y : minY + (this.unitWidth + this.gutter) * 2
33373         });
33374             
33375         return pos;
33376         
33377     },
33378     
33379     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33380     {
33381         var pos = [];
33382         
33383         if(box[0].size == 'xs'){
33384             
33385             pos.push({
33386                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33387                 y : minY
33388             });
33389
33390             pos.push({
33391                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33392                 y : minY
33393             });
33394             
33395             pos.push({
33396                 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),
33397                 y : minY
33398             });
33399             
33400             pos.push({
33401                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33402                 y : minY + (this.unitWidth + this.gutter) * 1
33403             });
33404             
33405             return pos;
33406             
33407         }
33408         
33409         pos.push({
33410             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33411             y : minY
33412         });
33413         
33414         pos.push({
33415             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33416             y : minY + (this.unitWidth + this.gutter) * 2
33417         });
33418         
33419         pos.push({
33420             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33421             y : minY + (this.unitWidth + this.gutter) * 2
33422         });
33423         
33424         pos.push({
33425             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),
33426             y : minY + (this.unitWidth + this.gutter) * 2
33427         });
33428
33429         return pos;
33430         
33431     },
33432     
33433     /**
33434     * remove a Masonry Brick
33435     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33436     */
33437     removeBrick : function(brick_id)
33438     {
33439         if (!brick_id) {
33440             return;
33441         }
33442         
33443         for (var i = 0; i<this.bricks.length; i++) {
33444             if (this.bricks[i].id == brick_id) {
33445                 this.bricks.splice(i,1);
33446                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33447                 this.initial();
33448             }
33449         }
33450     },
33451     
33452     /**
33453     * adds a Masonry Brick
33454     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33455     */
33456     addBrick : function(cfg)
33457     {
33458         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33459         //this.register(cn);
33460         cn.parentId = this.id;
33461         cn.render(this.el);
33462         return cn;
33463     },
33464     
33465     /**
33466     * register a Masonry Brick
33467     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33468     */
33469     
33470     register : function(brick)
33471     {
33472         this.bricks.push(brick);
33473         brick.masonryId = this.id;
33474     },
33475     
33476     /**
33477     * clear all the Masonry Brick
33478     */
33479     clearAll : function()
33480     {
33481         this.bricks = [];
33482         //this.getChildContainer().dom.innerHTML = "";
33483         this.el.dom.innerHTML = '';
33484     },
33485     
33486     getSelected : function()
33487     {
33488         if (!this.selectedBrick) {
33489             return false;
33490         }
33491         
33492         return this.selectedBrick;
33493     }
33494 });
33495
33496 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33497     
33498     groups: {},
33499      /**
33500     * register a Masonry Layout
33501     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33502     */
33503     
33504     register : function(layout)
33505     {
33506         this.groups[layout.id] = layout;
33507     },
33508     /**
33509     * fetch a  Masonry Layout based on the masonry layout ID
33510     * @param {string} the masonry layout to add
33511     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33512     */
33513     
33514     get: function(layout_id) {
33515         if (typeof(this.groups[layout_id]) == 'undefined') {
33516             return false;
33517         }
33518         return this.groups[layout_id] ;
33519     }
33520     
33521     
33522     
33523 });
33524
33525  
33526
33527  /**
33528  *
33529  * This is based on 
33530  * http://masonry.desandro.com
33531  *
33532  * The idea is to render all the bricks based on vertical width...
33533  *
33534  * The original code extends 'outlayer' - we might need to use that....
33535  * 
33536  */
33537
33538
33539 /**
33540  * @class Roo.bootstrap.LayoutMasonryAuto
33541  * @extends Roo.bootstrap.Component
33542  * Bootstrap Layout Masonry class
33543  * 
33544  * @constructor
33545  * Create a new Element
33546  * @param {Object} config The config object
33547  */
33548
33549 Roo.bootstrap.LayoutMasonryAuto = function(config){
33550     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33551 };
33552
33553 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33554     
33555       /**
33556      * @cfg {Boolean} isFitWidth  - resize the width..
33557      */   
33558     isFitWidth : false,  // options..
33559     /**
33560      * @cfg {Boolean} isOriginLeft = left align?
33561      */   
33562     isOriginLeft : true,
33563     /**
33564      * @cfg {Boolean} isOriginTop = top align?
33565      */   
33566     isOriginTop : false,
33567     /**
33568      * @cfg {Boolean} isLayoutInstant = no animation?
33569      */   
33570     isLayoutInstant : false, // needed?
33571     /**
33572      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33573      */   
33574     isResizingContainer : true,
33575     /**
33576      * @cfg {Number} columnWidth  width of the columns 
33577      */   
33578     
33579     columnWidth : 0,
33580     
33581     /**
33582      * @cfg {Number} maxCols maximum number of columns
33583      */   
33584     
33585     maxCols: 0,
33586     /**
33587      * @cfg {Number} padHeight padding below box..
33588      */   
33589     
33590     padHeight : 10, 
33591     
33592     /**
33593      * @cfg {Boolean} isAutoInitial defalut true
33594      */   
33595     
33596     isAutoInitial : true, 
33597     
33598     // private?
33599     gutter : 0,
33600     
33601     containerWidth: 0,
33602     initialColumnWidth : 0,
33603     currentSize : null,
33604     
33605     colYs : null, // array.
33606     maxY : 0,
33607     padWidth: 10,
33608     
33609     
33610     tag: 'div',
33611     cls: '',
33612     bricks: null, //CompositeElement
33613     cols : 0, // array?
33614     // element : null, // wrapped now this.el
33615     _isLayoutInited : null, 
33616     
33617     
33618     getAutoCreate : function(){
33619         
33620         var cfg = {
33621             tag: this.tag,
33622             cls: 'blog-masonary-wrapper ' + this.cls,
33623             cn : {
33624                 cls : 'mas-boxes masonary'
33625             }
33626         };
33627         
33628         return cfg;
33629     },
33630     
33631     getChildContainer: function( )
33632     {
33633         if (this.boxesEl) {
33634             return this.boxesEl;
33635         }
33636         
33637         this.boxesEl = this.el.select('.mas-boxes').first();
33638         
33639         return this.boxesEl;
33640     },
33641     
33642     
33643     initEvents : function()
33644     {
33645         var _this = this;
33646         
33647         if(this.isAutoInitial){
33648             Roo.log('hook children rendered');
33649             this.on('childrenrendered', function() {
33650                 Roo.log('children rendered');
33651                 _this.initial();
33652             } ,this);
33653         }
33654         
33655     },
33656     
33657     initial : function()
33658     {
33659         this.reloadItems();
33660
33661         this.currentSize = this.el.getBox(true);
33662
33663         /// was window resize... - let's see if this works..
33664         Roo.EventManager.onWindowResize(this.resize, this); 
33665
33666         if(!this.isAutoInitial){
33667             this.layout();
33668             return;
33669         }
33670         
33671         this.layout.defer(500,this);
33672     },
33673     
33674     reloadItems: function()
33675     {
33676         this.bricks = this.el.select('.masonry-brick', true);
33677         
33678         this.bricks.each(function(b) {
33679             //Roo.log(b.getSize());
33680             if (!b.attr('originalwidth')) {
33681                 b.attr('originalwidth',  b.getSize().width);
33682             }
33683             
33684         });
33685         
33686         Roo.log(this.bricks.elements.length);
33687     },
33688     
33689     resize : function()
33690     {
33691         Roo.log('resize');
33692         var cs = this.el.getBox(true);
33693         
33694         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33695             Roo.log("no change in with or X");
33696             return;
33697         }
33698         this.currentSize = cs;
33699         this.layout();
33700     },
33701     
33702     layout : function()
33703     {
33704          Roo.log('layout');
33705         this._resetLayout();
33706         //this._manageStamps();
33707       
33708         // don't animate first layout
33709         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33710         this.layoutItems( isInstant );
33711       
33712         // flag for initalized
33713         this._isLayoutInited = true;
33714     },
33715     
33716     layoutItems : function( isInstant )
33717     {
33718         //var items = this._getItemsForLayout( this.items );
33719         // original code supports filtering layout items.. we just ignore it..
33720         
33721         this._layoutItems( this.bricks , isInstant );
33722       
33723         this._postLayout();
33724     },
33725     _layoutItems : function ( items , isInstant)
33726     {
33727        //this.fireEvent( 'layout', this, items );
33728     
33729
33730         if ( !items || !items.elements.length ) {
33731           // no items, emit event with empty array
33732             return;
33733         }
33734
33735         var queue = [];
33736         items.each(function(item) {
33737             Roo.log("layout item");
33738             Roo.log(item);
33739             // get x/y object from method
33740             var position = this._getItemLayoutPosition( item );
33741             // enqueue
33742             position.item = item;
33743             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33744             queue.push( position );
33745         }, this);
33746       
33747         this._processLayoutQueue( queue );
33748     },
33749     /** Sets position of item in DOM
33750     * @param {Element} item
33751     * @param {Number} x - horizontal position
33752     * @param {Number} y - vertical position
33753     * @param {Boolean} isInstant - disables transitions
33754     */
33755     _processLayoutQueue : function( queue )
33756     {
33757         for ( var i=0, len = queue.length; i < len; i++ ) {
33758             var obj = queue[i];
33759             obj.item.position('absolute');
33760             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33761         }
33762     },
33763       
33764     
33765     /**
33766     * Any logic you want to do after each layout,
33767     * i.e. size the container
33768     */
33769     _postLayout : function()
33770     {
33771         this.resizeContainer();
33772     },
33773     
33774     resizeContainer : function()
33775     {
33776         if ( !this.isResizingContainer ) {
33777             return;
33778         }
33779         var size = this._getContainerSize();
33780         if ( size ) {
33781             this.el.setSize(size.width,size.height);
33782             this.boxesEl.setSize(size.width,size.height);
33783         }
33784     },
33785     
33786     
33787     
33788     _resetLayout : function()
33789     {
33790         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33791         this.colWidth = this.el.getWidth();
33792         //this.gutter = this.el.getWidth(); 
33793         
33794         this.measureColumns();
33795
33796         // reset column Y
33797         var i = this.cols;
33798         this.colYs = [];
33799         while (i--) {
33800             this.colYs.push( 0 );
33801         }
33802     
33803         this.maxY = 0;
33804     },
33805
33806     measureColumns : function()
33807     {
33808         this.getContainerWidth();
33809       // if columnWidth is 0, default to outerWidth of first item
33810         if ( !this.columnWidth ) {
33811             var firstItem = this.bricks.first();
33812             Roo.log(firstItem);
33813             this.columnWidth  = this.containerWidth;
33814             if (firstItem && firstItem.attr('originalwidth') ) {
33815                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33816             }
33817             // columnWidth fall back to item of first element
33818             Roo.log("set column width?");
33819                         this.initialColumnWidth = this.columnWidth  ;
33820
33821             // if first elem has no width, default to size of container
33822             
33823         }
33824         
33825         
33826         if (this.initialColumnWidth) {
33827             this.columnWidth = this.initialColumnWidth;
33828         }
33829         
33830         
33831             
33832         // column width is fixed at the top - however if container width get's smaller we should
33833         // reduce it...
33834         
33835         // this bit calcs how man columns..
33836             
33837         var columnWidth = this.columnWidth += this.gutter;
33838       
33839         // calculate columns
33840         var containerWidth = this.containerWidth + this.gutter;
33841         
33842         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33843         // fix rounding errors, typically with gutters
33844         var excess = columnWidth - containerWidth % columnWidth;
33845         
33846         
33847         // if overshoot is less than a pixel, round up, otherwise floor it
33848         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33849         cols = Math[ mathMethod ]( cols );
33850         this.cols = Math.max( cols, 1 );
33851         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33852         
33853          // padding positioning..
33854         var totalColWidth = this.cols * this.columnWidth;
33855         var padavail = this.containerWidth - totalColWidth;
33856         // so for 2 columns - we need 3 'pads'
33857         
33858         var padNeeded = (1+this.cols) * this.padWidth;
33859         
33860         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33861         
33862         this.columnWidth += padExtra
33863         //this.padWidth = Math.floor(padavail /  ( this.cols));
33864         
33865         // adjust colum width so that padding is fixed??
33866         
33867         // we have 3 columns ... total = width * 3
33868         // we have X left over... that should be used by 
33869         
33870         //if (this.expandC) {
33871             
33872         //}
33873         
33874         
33875         
33876     },
33877     
33878     getContainerWidth : function()
33879     {
33880        /* // container is parent if fit width
33881         var container = this.isFitWidth ? this.element.parentNode : this.element;
33882         // check that this.size and size are there
33883         // IE8 triggers resize on body size change, so they might not be
33884         
33885         var size = getSize( container );  //FIXME
33886         this.containerWidth = size && size.innerWidth; //FIXME
33887         */
33888          
33889         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33890         
33891     },
33892     
33893     _getItemLayoutPosition : function( item )  // what is item?
33894     {
33895         // we resize the item to our columnWidth..
33896       
33897         item.setWidth(this.columnWidth);
33898         item.autoBoxAdjust  = false;
33899         
33900         var sz = item.getSize();
33901  
33902         // how many columns does this brick span
33903         var remainder = this.containerWidth % this.columnWidth;
33904         
33905         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33906         // round if off by 1 pixel, otherwise use ceil
33907         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33908         colSpan = Math.min( colSpan, this.cols );
33909         
33910         // normally this should be '1' as we dont' currently allow multi width columns..
33911         
33912         var colGroup = this._getColGroup( colSpan );
33913         // get the minimum Y value from the columns
33914         var minimumY = Math.min.apply( Math, colGroup );
33915         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33916         
33917         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33918          
33919         // position the brick
33920         var position = {
33921             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33922             y: this.currentSize.y + minimumY + this.padHeight
33923         };
33924         
33925         Roo.log(position);
33926         // apply setHeight to necessary columns
33927         var setHeight = minimumY + sz.height + this.padHeight;
33928         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33929         
33930         var setSpan = this.cols + 1 - colGroup.length;
33931         for ( var i = 0; i < setSpan; i++ ) {
33932           this.colYs[ shortColIndex + i ] = setHeight ;
33933         }
33934       
33935         return position;
33936     },
33937     
33938     /**
33939      * @param {Number} colSpan - number of columns the element spans
33940      * @returns {Array} colGroup
33941      */
33942     _getColGroup : function( colSpan )
33943     {
33944         if ( colSpan < 2 ) {
33945           // if brick spans only one column, use all the column Ys
33946           return this.colYs;
33947         }
33948       
33949         var colGroup = [];
33950         // how many different places could this brick fit horizontally
33951         var groupCount = this.cols + 1 - colSpan;
33952         // for each group potential horizontal position
33953         for ( var i = 0; i < groupCount; i++ ) {
33954           // make an array of colY values for that one group
33955           var groupColYs = this.colYs.slice( i, i + colSpan );
33956           // and get the max value of the array
33957           colGroup[i] = Math.max.apply( Math, groupColYs );
33958         }
33959         return colGroup;
33960     },
33961     /*
33962     _manageStamp : function( stamp )
33963     {
33964         var stampSize =  stamp.getSize();
33965         var offset = stamp.getBox();
33966         // get the columns that this stamp affects
33967         var firstX = this.isOriginLeft ? offset.x : offset.right;
33968         var lastX = firstX + stampSize.width;
33969         var firstCol = Math.floor( firstX / this.columnWidth );
33970         firstCol = Math.max( 0, firstCol );
33971         
33972         var lastCol = Math.floor( lastX / this.columnWidth );
33973         // lastCol should not go over if multiple of columnWidth #425
33974         lastCol -= lastX % this.columnWidth ? 0 : 1;
33975         lastCol = Math.min( this.cols - 1, lastCol );
33976         
33977         // set colYs to bottom of the stamp
33978         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33979             stampSize.height;
33980             
33981         for ( var i = firstCol; i <= lastCol; i++ ) {
33982           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33983         }
33984     },
33985     */
33986     
33987     _getContainerSize : function()
33988     {
33989         this.maxY = Math.max.apply( Math, this.colYs );
33990         var size = {
33991             height: this.maxY
33992         };
33993       
33994         if ( this.isFitWidth ) {
33995             size.width = this._getContainerFitWidth();
33996         }
33997       
33998         return size;
33999     },
34000     
34001     _getContainerFitWidth : function()
34002     {
34003         var unusedCols = 0;
34004         // count unused columns
34005         var i = this.cols;
34006         while ( --i ) {
34007           if ( this.colYs[i] !== 0 ) {
34008             break;
34009           }
34010           unusedCols++;
34011         }
34012         // fit container to columns that have been used
34013         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34014     },
34015     
34016     needsResizeLayout : function()
34017     {
34018         var previousWidth = this.containerWidth;
34019         this.getContainerWidth();
34020         return previousWidth !== this.containerWidth;
34021     }
34022  
34023 });
34024
34025  
34026
34027  /*
34028  * - LGPL
34029  *
34030  * element
34031  * 
34032  */
34033
34034 /**
34035  * @class Roo.bootstrap.MasonryBrick
34036  * @extends Roo.bootstrap.Component
34037  * Bootstrap MasonryBrick class
34038  * 
34039  * @constructor
34040  * Create a new MasonryBrick
34041  * @param {Object} config The config object
34042  */
34043
34044 Roo.bootstrap.MasonryBrick = function(config){
34045     
34046     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34047     
34048     Roo.bootstrap.MasonryBrick.register(this);
34049     
34050     this.addEvents({
34051         // raw events
34052         /**
34053          * @event click
34054          * When a MasonryBrick is clcik
34055          * @param {Roo.bootstrap.MasonryBrick} this
34056          * @param {Roo.EventObject} e
34057          */
34058         "click" : true
34059     });
34060 };
34061
34062 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34063     
34064     /**
34065      * @cfg {String} title
34066      */   
34067     title : '',
34068     /**
34069      * @cfg {String} html
34070      */   
34071     html : '',
34072     /**
34073      * @cfg {String} bgimage
34074      */   
34075     bgimage : '',
34076     /**
34077      * @cfg {String} videourl
34078      */   
34079     videourl : '',
34080     /**
34081      * @cfg {String} cls
34082      */   
34083     cls : '',
34084     /**
34085      * @cfg {String} href
34086      */   
34087     href : '',
34088     /**
34089      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34090      */   
34091     size : 'xs',
34092     
34093     /**
34094      * @cfg {String} placetitle (center|bottom)
34095      */   
34096     placetitle : '',
34097     
34098     /**
34099      * @cfg {Boolean} isFitContainer defalut true
34100      */   
34101     isFitContainer : true, 
34102     
34103     /**
34104      * @cfg {Boolean} preventDefault defalut false
34105      */   
34106     preventDefault : false, 
34107     
34108     /**
34109      * @cfg {Boolean} inverse defalut false
34110      */   
34111     maskInverse : false, 
34112     
34113     getAutoCreate : function()
34114     {
34115         if(!this.isFitContainer){
34116             return this.getSplitAutoCreate();
34117         }
34118         
34119         var cls = 'masonry-brick masonry-brick-full';
34120         
34121         if(this.href.length){
34122             cls += ' masonry-brick-link';
34123         }
34124         
34125         if(this.bgimage.length){
34126             cls += ' masonry-brick-image';
34127         }
34128         
34129         if(this.maskInverse){
34130             cls += ' mask-inverse';
34131         }
34132         
34133         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34134             cls += ' enable-mask';
34135         }
34136         
34137         if(this.size){
34138             cls += ' masonry-' + this.size + '-brick';
34139         }
34140         
34141         if(this.placetitle.length){
34142             
34143             switch (this.placetitle) {
34144                 case 'center' :
34145                     cls += ' masonry-center-title';
34146                     break;
34147                 case 'bottom' :
34148                     cls += ' masonry-bottom-title';
34149                     break;
34150                 default:
34151                     break;
34152             }
34153             
34154         } else {
34155             if(!this.html.length && !this.bgimage.length){
34156                 cls += ' masonry-center-title';
34157             }
34158
34159             if(!this.html.length && this.bgimage.length){
34160                 cls += ' masonry-bottom-title';
34161             }
34162         }
34163         
34164         if(this.cls){
34165             cls += ' ' + this.cls;
34166         }
34167         
34168         var cfg = {
34169             tag: (this.href.length) ? 'a' : 'div',
34170             cls: cls,
34171             cn: [
34172                 {
34173                     tag: 'div',
34174                     cls: 'masonry-brick-mask'
34175                 },
34176                 {
34177                     tag: 'div',
34178                     cls: 'masonry-brick-paragraph',
34179                     cn: []
34180                 }
34181             ]
34182         };
34183         
34184         if(this.href.length){
34185             cfg.href = this.href;
34186         }
34187         
34188         var cn = cfg.cn[1].cn;
34189         
34190         if(this.title.length){
34191             cn.push({
34192                 tag: 'h4',
34193                 cls: 'masonry-brick-title',
34194                 html: this.title
34195             });
34196         }
34197         
34198         if(this.html.length){
34199             cn.push({
34200                 tag: 'p',
34201                 cls: 'masonry-brick-text',
34202                 html: this.html
34203             });
34204         }
34205         
34206         if (!this.title.length && !this.html.length) {
34207             cfg.cn[1].cls += ' hide';
34208         }
34209         
34210         if(this.bgimage.length){
34211             cfg.cn.push({
34212                 tag: 'img',
34213                 cls: 'masonry-brick-image-view',
34214                 src: this.bgimage
34215             });
34216         }
34217         
34218         if(this.videourl.length){
34219             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34220             // youtube support only?
34221             cfg.cn.push({
34222                 tag: 'iframe',
34223                 cls: 'masonry-brick-image-view',
34224                 src: vurl,
34225                 frameborder : 0,
34226                 allowfullscreen : true
34227             });
34228         }
34229         
34230         return cfg;
34231         
34232     },
34233     
34234     getSplitAutoCreate : function()
34235     {
34236         var cls = 'masonry-brick masonry-brick-split';
34237         
34238         if(this.href.length){
34239             cls += ' masonry-brick-link';
34240         }
34241         
34242         if(this.bgimage.length){
34243             cls += ' masonry-brick-image';
34244         }
34245         
34246         if(this.size){
34247             cls += ' masonry-' + this.size + '-brick';
34248         }
34249         
34250         switch (this.placetitle) {
34251             case 'center' :
34252                 cls += ' masonry-center-title';
34253                 break;
34254             case 'bottom' :
34255                 cls += ' masonry-bottom-title';
34256                 break;
34257             default:
34258                 if(!this.bgimage.length){
34259                     cls += ' masonry-center-title';
34260                 }
34261
34262                 if(this.bgimage.length){
34263                     cls += ' masonry-bottom-title';
34264                 }
34265                 break;
34266         }
34267         
34268         if(this.cls){
34269             cls += ' ' + this.cls;
34270         }
34271         
34272         var cfg = {
34273             tag: (this.href.length) ? 'a' : 'div',
34274             cls: cls,
34275             cn: [
34276                 {
34277                     tag: 'div',
34278                     cls: 'masonry-brick-split-head',
34279                     cn: [
34280                         {
34281                             tag: 'div',
34282                             cls: 'masonry-brick-paragraph',
34283                             cn: []
34284                         }
34285                     ]
34286                 },
34287                 {
34288                     tag: 'div',
34289                     cls: 'masonry-brick-split-body',
34290                     cn: []
34291                 }
34292             ]
34293         };
34294         
34295         if(this.href.length){
34296             cfg.href = this.href;
34297         }
34298         
34299         if(this.title.length){
34300             cfg.cn[0].cn[0].cn.push({
34301                 tag: 'h4',
34302                 cls: 'masonry-brick-title',
34303                 html: this.title
34304             });
34305         }
34306         
34307         if(this.html.length){
34308             cfg.cn[1].cn.push({
34309                 tag: 'p',
34310                 cls: 'masonry-brick-text',
34311                 html: this.html
34312             });
34313         }
34314
34315         if(this.bgimage.length){
34316             cfg.cn[0].cn.push({
34317                 tag: 'img',
34318                 cls: 'masonry-brick-image-view',
34319                 src: this.bgimage
34320             });
34321         }
34322         
34323         if(this.videourl.length){
34324             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34325             // youtube support only?
34326             cfg.cn[0].cn.cn.push({
34327                 tag: 'iframe',
34328                 cls: 'masonry-brick-image-view',
34329                 src: vurl,
34330                 frameborder : 0,
34331                 allowfullscreen : true
34332             });
34333         }
34334         
34335         return cfg;
34336     },
34337     
34338     initEvents: function() 
34339     {
34340         switch (this.size) {
34341             case 'xs' :
34342                 this.x = 1;
34343                 this.y = 1;
34344                 break;
34345             case 'sm' :
34346                 this.x = 2;
34347                 this.y = 2;
34348                 break;
34349             case 'md' :
34350             case 'md-left' :
34351             case 'md-right' :
34352                 this.x = 3;
34353                 this.y = 3;
34354                 break;
34355             case 'tall' :
34356                 this.x = 2;
34357                 this.y = 3;
34358                 break;
34359             case 'wide' :
34360                 this.x = 3;
34361                 this.y = 2;
34362                 break;
34363             case 'wide-thin' :
34364                 this.x = 3;
34365                 this.y = 1;
34366                 break;
34367                         
34368             default :
34369                 break;
34370         }
34371         
34372         if(Roo.isTouch){
34373             this.el.on('touchstart', this.onTouchStart, this);
34374             this.el.on('touchmove', this.onTouchMove, this);
34375             this.el.on('touchend', this.onTouchEnd, this);
34376             this.el.on('contextmenu', this.onContextMenu, this);
34377         } else {
34378             this.el.on('mouseenter'  ,this.enter, this);
34379             this.el.on('mouseleave', this.leave, this);
34380             this.el.on('click', this.onClick, this);
34381         }
34382         
34383         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34384             this.parent().bricks.push(this);   
34385         }
34386         
34387     },
34388     
34389     onClick: function(e, el)
34390     {
34391         var time = this.endTimer - this.startTimer;
34392         // Roo.log(e.preventDefault());
34393         if(Roo.isTouch){
34394             if(time > 1000){
34395                 e.preventDefault();
34396                 return;
34397             }
34398         }
34399         
34400         if(!this.preventDefault){
34401             return;
34402         }
34403         
34404         e.preventDefault();
34405         
34406         if (this.activeClass != '') {
34407             this.selectBrick();
34408         }
34409         
34410         this.fireEvent('click', this, e);
34411     },
34412     
34413     enter: function(e, el)
34414     {
34415         e.preventDefault();
34416         
34417         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34418             return;
34419         }
34420         
34421         if(this.bgimage.length && this.html.length){
34422             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34423         }
34424     },
34425     
34426     leave: function(e, el)
34427     {
34428         e.preventDefault();
34429         
34430         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34431             return;
34432         }
34433         
34434         if(this.bgimage.length && this.html.length){
34435             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34436         }
34437     },
34438     
34439     onTouchStart: function(e, el)
34440     {
34441 //        e.preventDefault();
34442         
34443         this.touchmoved = false;
34444         
34445         if(!this.isFitContainer){
34446             return;
34447         }
34448         
34449         if(!this.bgimage.length || !this.html.length){
34450             return;
34451         }
34452         
34453         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34454         
34455         this.timer = new Date().getTime();
34456         
34457     },
34458     
34459     onTouchMove: function(e, el)
34460     {
34461         this.touchmoved = true;
34462     },
34463     
34464     onContextMenu : function(e,el)
34465     {
34466         e.preventDefault();
34467         e.stopPropagation();
34468         return false;
34469     },
34470     
34471     onTouchEnd: function(e, el)
34472     {
34473 //        e.preventDefault();
34474         
34475         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34476         
34477             this.leave(e,el);
34478             
34479             return;
34480         }
34481         
34482         if(!this.bgimage.length || !this.html.length){
34483             
34484             if(this.href.length){
34485                 window.location.href = this.href;
34486             }
34487             
34488             return;
34489         }
34490         
34491         if(!this.isFitContainer){
34492             return;
34493         }
34494         
34495         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34496         
34497         window.location.href = this.href;
34498     },
34499     
34500     //selection on single brick only
34501     selectBrick : function() {
34502         
34503         if (!this.parentId) {
34504             return;
34505         }
34506         
34507         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34508         var index = m.selectedBrick.indexOf(this.id);
34509         
34510         if ( index > -1) {
34511             m.selectedBrick.splice(index,1);
34512             this.el.removeClass(this.activeClass);
34513             return;
34514         }
34515         
34516         for(var i = 0; i < m.selectedBrick.length; i++) {
34517             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34518             b.el.removeClass(b.activeClass);
34519         }
34520         
34521         m.selectedBrick = [];
34522         
34523         m.selectedBrick.push(this.id);
34524         this.el.addClass(this.activeClass);
34525         return;
34526     },
34527     
34528     isSelected : function(){
34529         return this.el.hasClass(this.activeClass);
34530         
34531     }
34532 });
34533
34534 Roo.apply(Roo.bootstrap.MasonryBrick, {
34535     
34536     //groups: {},
34537     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34538      /**
34539     * register a Masonry Brick
34540     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34541     */
34542     
34543     register : function(brick)
34544     {
34545         //this.groups[brick.id] = brick;
34546         this.groups.add(brick.id, brick);
34547     },
34548     /**
34549     * fetch a  masonry brick based on the masonry brick ID
34550     * @param {string} the masonry brick to add
34551     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34552     */
34553     
34554     get: function(brick_id) 
34555     {
34556         // if (typeof(this.groups[brick_id]) == 'undefined') {
34557         //     return false;
34558         // }
34559         // return this.groups[brick_id] ;
34560         
34561         if(this.groups.key(brick_id)) {
34562             return this.groups.key(brick_id);
34563         }
34564         
34565         return false;
34566     }
34567     
34568     
34569     
34570 });
34571
34572  /*
34573  * - LGPL
34574  *
34575  * element
34576  * 
34577  */
34578
34579 /**
34580  * @class Roo.bootstrap.Brick
34581  * @extends Roo.bootstrap.Component
34582  * Bootstrap Brick class
34583  * 
34584  * @constructor
34585  * Create a new Brick
34586  * @param {Object} config The config object
34587  */
34588
34589 Roo.bootstrap.Brick = function(config){
34590     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34591     
34592     this.addEvents({
34593         // raw events
34594         /**
34595          * @event click
34596          * When a Brick is click
34597          * @param {Roo.bootstrap.Brick} this
34598          * @param {Roo.EventObject} e
34599          */
34600         "click" : true
34601     });
34602 };
34603
34604 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34605     
34606     /**
34607      * @cfg {String} title
34608      */   
34609     title : '',
34610     /**
34611      * @cfg {String} html
34612      */   
34613     html : '',
34614     /**
34615      * @cfg {String} bgimage
34616      */   
34617     bgimage : '',
34618     /**
34619      * @cfg {String} cls
34620      */   
34621     cls : '',
34622     /**
34623      * @cfg {String} href
34624      */   
34625     href : '',
34626     /**
34627      * @cfg {String} video
34628      */   
34629     video : '',
34630     /**
34631      * @cfg {Boolean} square
34632      */   
34633     square : true,
34634     
34635     getAutoCreate : function()
34636     {
34637         var cls = 'roo-brick';
34638         
34639         if(this.href.length){
34640             cls += ' roo-brick-link';
34641         }
34642         
34643         if(this.bgimage.length){
34644             cls += ' roo-brick-image';
34645         }
34646         
34647         if(!this.html.length && !this.bgimage.length){
34648             cls += ' roo-brick-center-title';
34649         }
34650         
34651         if(!this.html.length && this.bgimage.length){
34652             cls += ' roo-brick-bottom-title';
34653         }
34654         
34655         if(this.cls){
34656             cls += ' ' + this.cls;
34657         }
34658         
34659         var cfg = {
34660             tag: (this.href.length) ? 'a' : 'div',
34661             cls: cls,
34662             cn: [
34663                 {
34664                     tag: 'div',
34665                     cls: 'roo-brick-paragraph',
34666                     cn: []
34667                 }
34668             ]
34669         };
34670         
34671         if(this.href.length){
34672             cfg.href = this.href;
34673         }
34674         
34675         var cn = cfg.cn[0].cn;
34676         
34677         if(this.title.length){
34678             cn.push({
34679                 tag: 'h4',
34680                 cls: 'roo-brick-title',
34681                 html: this.title
34682             });
34683         }
34684         
34685         if(this.html.length){
34686             cn.push({
34687                 tag: 'p',
34688                 cls: 'roo-brick-text',
34689                 html: this.html
34690             });
34691         } else {
34692             cn.cls += ' hide';
34693         }
34694         
34695         if(this.bgimage.length){
34696             cfg.cn.push({
34697                 tag: 'img',
34698                 cls: 'roo-brick-image-view',
34699                 src: this.bgimage
34700             });
34701         }
34702         
34703         return cfg;
34704     },
34705     
34706     initEvents: function() 
34707     {
34708         if(this.title.length || this.html.length){
34709             this.el.on('mouseenter'  ,this.enter, this);
34710             this.el.on('mouseleave', this.leave, this);
34711         }
34712         
34713         Roo.EventManager.onWindowResize(this.resize, this); 
34714         
34715         if(this.bgimage.length){
34716             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34717             this.imageEl.on('load', this.onImageLoad, this);
34718             return;
34719         }
34720         
34721         this.resize();
34722     },
34723     
34724     onImageLoad : function()
34725     {
34726         this.resize();
34727     },
34728     
34729     resize : function()
34730     {
34731         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34732         
34733         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34734         
34735         if(this.bgimage.length){
34736             var image = this.el.select('.roo-brick-image-view', true).first();
34737             
34738             image.setWidth(paragraph.getWidth());
34739             
34740             if(this.square){
34741                 image.setHeight(paragraph.getWidth());
34742             }
34743             
34744             this.el.setHeight(image.getHeight());
34745             paragraph.setHeight(image.getHeight());
34746             
34747         }
34748         
34749     },
34750     
34751     enter: function(e, el)
34752     {
34753         e.preventDefault();
34754         
34755         if(this.bgimage.length){
34756             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34757             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34758         }
34759     },
34760     
34761     leave: function(e, el)
34762     {
34763         e.preventDefault();
34764         
34765         if(this.bgimage.length){
34766             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34767             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34768         }
34769     }
34770     
34771 });
34772
34773  
34774
34775  /*
34776  * - LGPL
34777  *
34778  * Number field 
34779  */
34780
34781 /**
34782  * @class Roo.bootstrap.NumberField
34783  * @extends Roo.bootstrap.Input
34784  * Bootstrap NumberField class
34785  * 
34786  * 
34787  * 
34788  * 
34789  * @constructor
34790  * Create a new NumberField
34791  * @param {Object} config The config object
34792  */
34793
34794 Roo.bootstrap.NumberField = function(config){
34795     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34796 };
34797
34798 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34799     
34800     /**
34801      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34802      */
34803     allowDecimals : true,
34804     /**
34805      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34806      */
34807     decimalSeparator : ".",
34808     /**
34809      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34810      */
34811     decimalPrecision : 2,
34812     /**
34813      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34814      */
34815     allowNegative : true,
34816     
34817     /**
34818      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34819      */
34820     allowZero: true,
34821     /**
34822      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34823      */
34824     minValue : Number.NEGATIVE_INFINITY,
34825     /**
34826      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34827      */
34828     maxValue : Number.MAX_VALUE,
34829     /**
34830      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34831      */
34832     minText : "The minimum value for this field is {0}",
34833     /**
34834      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34835      */
34836     maxText : "The maximum value for this field is {0}",
34837     /**
34838      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34839      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34840      */
34841     nanText : "{0} is not a valid number",
34842     /**
34843      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34844      */
34845     thousandsDelimiter : false,
34846     /**
34847      * @cfg {String} valueAlign alignment of value
34848      */
34849     valueAlign : "left",
34850
34851     getAutoCreate : function()
34852     {
34853         var hiddenInput = {
34854             tag: 'input',
34855             type: 'hidden',
34856             id: Roo.id(),
34857             cls: 'hidden-number-input'
34858         };
34859         
34860         if (this.name) {
34861             hiddenInput.name = this.name;
34862         }
34863         
34864         this.name = '';
34865         
34866         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34867         
34868         this.name = hiddenInput.name;
34869         
34870         if(cfg.cn.length > 0) {
34871             cfg.cn.push(hiddenInput);
34872         }
34873         
34874         return cfg;
34875     },
34876
34877     // private
34878     initEvents : function()
34879     {   
34880         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34881         
34882         var allowed = "0123456789";
34883         
34884         if(this.allowDecimals){
34885             allowed += this.decimalSeparator;
34886         }
34887         
34888         if(this.allowNegative){
34889             allowed += "-";
34890         }
34891         
34892         if(this.thousandsDelimiter) {
34893             allowed += ",";
34894         }
34895         
34896         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34897         
34898         var keyPress = function(e){
34899             
34900             var k = e.getKey();
34901             
34902             var c = e.getCharCode();
34903             
34904             if(
34905                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34906                     allowed.indexOf(String.fromCharCode(c)) === -1
34907             ){
34908                 e.stopEvent();
34909                 return;
34910             }
34911             
34912             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34913                 return;
34914             }
34915             
34916             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34917                 e.stopEvent();
34918             }
34919         };
34920         
34921         this.el.on("keypress", keyPress, this);
34922     },
34923     
34924     validateValue : function(value)
34925     {
34926         
34927         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34928             return false;
34929         }
34930         
34931         var num = this.parseValue(value);
34932         
34933         if(isNaN(num)){
34934             this.markInvalid(String.format(this.nanText, value));
34935             return false;
34936         }
34937         
34938         if(num < this.minValue){
34939             this.markInvalid(String.format(this.minText, this.minValue));
34940             return false;
34941         }
34942         
34943         if(num > this.maxValue){
34944             this.markInvalid(String.format(this.maxText, this.maxValue));
34945             return false;
34946         }
34947         
34948         return true;
34949     },
34950
34951     getValue : function()
34952     {
34953         var v = this.hiddenEl().getValue();
34954         
34955         return this.fixPrecision(this.parseValue(v));
34956     },
34957
34958     parseValue : function(value)
34959     {
34960         if(this.thousandsDelimiter) {
34961             value += "";
34962             r = new RegExp(",", "g");
34963             value = value.replace(r, "");
34964         }
34965         
34966         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34967         return isNaN(value) ? '' : value;
34968     },
34969
34970     fixPrecision : function(value)
34971     {
34972         if(this.thousandsDelimiter) {
34973             value += "";
34974             r = new RegExp(",", "g");
34975             value = value.replace(r, "");
34976         }
34977         
34978         var nan = isNaN(value);
34979         
34980         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34981             return nan ? '' : value;
34982         }
34983         return parseFloat(value).toFixed(this.decimalPrecision);
34984     },
34985
34986     setValue : function(v)
34987     {
34988         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34989         
34990         this.value = v;
34991         
34992         if(this.rendered){
34993             
34994             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34995             
34996             this.inputEl().dom.value = (v == '') ? '' :
34997                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34998             
34999             if(!this.allowZero && v === '0') {
35000                 this.hiddenEl().dom.value = '';
35001                 this.inputEl().dom.value = '';
35002             }
35003             
35004             this.validate();
35005         }
35006     },
35007
35008     decimalPrecisionFcn : function(v)
35009     {
35010         return Math.floor(v);
35011     },
35012
35013     beforeBlur : function()
35014     {
35015         var v = this.parseValue(this.getRawValue());
35016         
35017         if(v || v === 0 || v === ''){
35018             this.setValue(v);
35019         }
35020     },
35021     
35022     hiddenEl : function()
35023     {
35024         return this.el.select('input.hidden-number-input',true).first();
35025     }
35026     
35027 });
35028
35029  
35030
35031 /*
35032 * Licence: LGPL
35033 */
35034
35035 /**
35036  * @class Roo.bootstrap.DocumentSlider
35037  * @extends Roo.bootstrap.Component
35038  * Bootstrap DocumentSlider class
35039  * 
35040  * @constructor
35041  * Create a new DocumentViewer
35042  * @param {Object} config The config object
35043  */
35044
35045 Roo.bootstrap.DocumentSlider = function(config){
35046     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35047     
35048     this.files = [];
35049     
35050     this.addEvents({
35051         /**
35052          * @event initial
35053          * Fire after initEvent
35054          * @param {Roo.bootstrap.DocumentSlider} this
35055          */
35056         "initial" : true,
35057         /**
35058          * @event update
35059          * Fire after update
35060          * @param {Roo.bootstrap.DocumentSlider} this
35061          */
35062         "update" : true,
35063         /**
35064          * @event click
35065          * Fire after click
35066          * @param {Roo.bootstrap.DocumentSlider} this
35067          */
35068         "click" : true
35069     });
35070 };
35071
35072 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35073     
35074     files : false,
35075     
35076     indicator : 0,
35077     
35078     getAutoCreate : function()
35079     {
35080         var cfg = {
35081             tag : 'div',
35082             cls : 'roo-document-slider',
35083             cn : [
35084                 {
35085                     tag : 'div',
35086                     cls : 'roo-document-slider-header',
35087                     cn : [
35088                         {
35089                             tag : 'div',
35090                             cls : 'roo-document-slider-header-title'
35091                         }
35092                     ]
35093                 },
35094                 {
35095                     tag : 'div',
35096                     cls : 'roo-document-slider-body',
35097                     cn : [
35098                         {
35099                             tag : 'div',
35100                             cls : 'roo-document-slider-prev',
35101                             cn : [
35102                                 {
35103                                     tag : 'i',
35104                                     cls : 'fa fa-chevron-left'
35105                                 }
35106                             ]
35107                         },
35108                         {
35109                             tag : 'div',
35110                             cls : 'roo-document-slider-thumb',
35111                             cn : [
35112                                 {
35113                                     tag : 'img',
35114                                     cls : 'roo-document-slider-image'
35115                                 }
35116                             ]
35117                         },
35118                         {
35119                             tag : 'div',
35120                             cls : 'roo-document-slider-next',
35121                             cn : [
35122                                 {
35123                                     tag : 'i',
35124                                     cls : 'fa fa-chevron-right'
35125                                 }
35126                             ]
35127                         }
35128                     ]
35129                 }
35130             ]
35131         };
35132         
35133         return cfg;
35134     },
35135     
35136     initEvents : function()
35137     {
35138         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35139         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35140         
35141         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35142         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35143         
35144         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35145         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35146         
35147         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35148         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35149         
35150         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35151         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35152         
35153         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35154         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35155         
35156         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35157         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35158         
35159         this.thumbEl.on('click', this.onClick, this);
35160         
35161         this.prevIndicator.on('click', this.prev, this);
35162         
35163         this.nextIndicator.on('click', this.next, this);
35164         
35165     },
35166     
35167     initial : function()
35168     {
35169         if(this.files.length){
35170             this.indicator = 1;
35171             this.update()
35172         }
35173         
35174         this.fireEvent('initial', this);
35175     },
35176     
35177     update : function()
35178     {
35179         this.imageEl.attr('src', this.files[this.indicator - 1]);
35180         
35181         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35182         
35183         this.prevIndicator.show();
35184         
35185         if(this.indicator == 1){
35186             this.prevIndicator.hide();
35187         }
35188         
35189         this.nextIndicator.show();
35190         
35191         if(this.indicator == this.files.length){
35192             this.nextIndicator.hide();
35193         }
35194         
35195         this.thumbEl.scrollTo('top');
35196         
35197         this.fireEvent('update', this);
35198     },
35199     
35200     onClick : function(e)
35201     {
35202         e.preventDefault();
35203         
35204         this.fireEvent('click', this);
35205     },
35206     
35207     prev : function(e)
35208     {
35209         e.preventDefault();
35210         
35211         this.indicator = Math.max(1, this.indicator - 1);
35212         
35213         this.update();
35214     },
35215     
35216     next : function(e)
35217     {
35218         e.preventDefault();
35219         
35220         this.indicator = Math.min(this.files.length, this.indicator + 1);
35221         
35222         this.update();
35223     }
35224 });
35225 /*
35226  * - LGPL
35227  *
35228  * RadioSet
35229  *
35230  *
35231  */
35232
35233 /**
35234  * @class Roo.bootstrap.RadioSet
35235  * @extends Roo.bootstrap.Input
35236  * Bootstrap RadioSet class
35237  * @cfg {String} indicatorpos (left|right) default left
35238  * @cfg {Boolean} inline (true|false) inline the element (default true)
35239  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35240  * @constructor
35241  * Create a new RadioSet
35242  * @param {Object} config The config object
35243  */
35244
35245 Roo.bootstrap.RadioSet = function(config){
35246     
35247     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35248     
35249     this.radioes = [];
35250     
35251     Roo.bootstrap.RadioSet.register(this);
35252     
35253     this.addEvents({
35254         /**
35255         * @event check
35256         * Fires when the element is checked or unchecked.
35257         * @param {Roo.bootstrap.RadioSet} this This radio
35258         * @param {Roo.bootstrap.Radio} item The checked item
35259         */
35260        check : true,
35261        /**
35262         * @event click
35263         * Fires when the element is click.
35264         * @param {Roo.bootstrap.RadioSet} this This radio set
35265         * @param {Roo.bootstrap.Radio} item The checked item
35266         * @param {Roo.EventObject} e The event object
35267         */
35268        click : true
35269     });
35270     
35271 };
35272
35273 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35274
35275     radioes : false,
35276     
35277     inline : true,
35278     
35279     weight : '',
35280     
35281     indicatorpos : 'left',
35282     
35283     getAutoCreate : function()
35284     {
35285         var label = {
35286             tag : 'label',
35287             cls : 'roo-radio-set-label',
35288             cn : [
35289                 {
35290                     tag : 'span',
35291                     html : this.fieldLabel
35292                 }
35293             ]
35294         };
35295         if (Roo.bootstrap.version == 3) {
35296             
35297             
35298             if(this.indicatorpos == 'left'){
35299                 label.cn.unshift({
35300                     tag : 'i',
35301                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35302                     tooltip : 'This field is required'
35303                 });
35304             } else {
35305                 label.cn.push({
35306                     tag : 'i',
35307                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35308                     tooltip : 'This field is required'
35309                 });
35310             }
35311         }
35312         var items = {
35313             tag : 'div',
35314             cls : 'roo-radio-set-items'
35315         };
35316         
35317         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35318         
35319         if (align === 'left' && this.fieldLabel.length) {
35320             
35321             items = {
35322                 cls : "roo-radio-set-right", 
35323                 cn: [
35324                     items
35325                 ]
35326             };
35327             
35328             if(this.labelWidth > 12){
35329                 label.style = "width: " + this.labelWidth + 'px';
35330             }
35331             
35332             if(this.labelWidth < 13 && this.labelmd == 0){
35333                 this.labelmd = this.labelWidth;
35334             }
35335             
35336             if(this.labellg > 0){
35337                 label.cls += ' col-lg-' + this.labellg;
35338                 items.cls += ' col-lg-' + (12 - this.labellg);
35339             }
35340             
35341             if(this.labelmd > 0){
35342                 label.cls += ' col-md-' + this.labelmd;
35343                 items.cls += ' col-md-' + (12 - this.labelmd);
35344             }
35345             
35346             if(this.labelsm > 0){
35347                 label.cls += ' col-sm-' + this.labelsm;
35348                 items.cls += ' col-sm-' + (12 - this.labelsm);
35349             }
35350             
35351             if(this.labelxs > 0){
35352                 label.cls += ' col-xs-' + this.labelxs;
35353                 items.cls += ' col-xs-' + (12 - this.labelxs);
35354             }
35355         }
35356         
35357         var cfg = {
35358             tag : 'div',
35359             cls : 'roo-radio-set',
35360             cn : [
35361                 {
35362                     tag : 'input',
35363                     cls : 'roo-radio-set-input',
35364                     type : 'hidden',
35365                     name : this.name,
35366                     value : this.value ? this.value :  ''
35367                 },
35368                 label,
35369                 items
35370             ]
35371         };
35372         
35373         if(this.weight.length){
35374             cfg.cls += ' roo-radio-' + this.weight;
35375         }
35376         
35377         if(this.inline) {
35378             cfg.cls += ' roo-radio-set-inline';
35379         }
35380         
35381         var settings=this;
35382         ['xs','sm','md','lg'].map(function(size){
35383             if (settings[size]) {
35384                 cfg.cls += ' col-' + size + '-' + settings[size];
35385             }
35386         });
35387         
35388         return cfg;
35389         
35390     },
35391
35392     initEvents : function()
35393     {
35394         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35395         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35396         
35397         if(!this.fieldLabel.length){
35398             this.labelEl.hide();
35399         }
35400         
35401         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35402         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35403         
35404         this.indicator = this.indicatorEl();
35405         
35406         if(this.indicator){
35407             this.indicator.addClass('invisible');
35408         }
35409         
35410         this.originalValue = this.getValue();
35411         
35412     },
35413     
35414     inputEl: function ()
35415     {
35416         return this.el.select('.roo-radio-set-input', true).first();
35417     },
35418     
35419     getChildContainer : function()
35420     {
35421         return this.itemsEl;
35422     },
35423     
35424     register : function(item)
35425     {
35426         this.radioes.push(item);
35427         
35428     },
35429     
35430     validate : function()
35431     {   
35432         if(this.getVisibilityEl().hasClass('hidden')){
35433             return true;
35434         }
35435         
35436         var valid = false;
35437         
35438         Roo.each(this.radioes, function(i){
35439             if(!i.checked){
35440                 return;
35441             }
35442             
35443             valid = true;
35444             return false;
35445         });
35446         
35447         if(this.allowBlank) {
35448             return true;
35449         }
35450         
35451         if(this.disabled || valid){
35452             this.markValid();
35453             return true;
35454         }
35455         
35456         this.markInvalid();
35457         return false;
35458         
35459     },
35460     
35461     markValid : function()
35462     {
35463         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35464             this.indicatorEl().removeClass('visible');
35465             this.indicatorEl().addClass('invisible');
35466         }
35467         
35468         
35469         if (Roo.bootstrap.version == 3) {
35470             this.el.removeClass([this.invalidClass, this.validClass]);
35471             this.el.addClass(this.validClass);
35472         } else {
35473             this.el.removeClass(['is-invalid','is-valid']);
35474             this.el.addClass(['is-valid']);
35475         }
35476         this.fireEvent('valid', this);
35477     },
35478     
35479     markInvalid : function(msg)
35480     {
35481         if(this.allowBlank || this.disabled){
35482             return;
35483         }
35484         
35485         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35486             this.indicatorEl().removeClass('invisible');
35487             this.indicatorEl().addClass('visible');
35488         }
35489         if (Roo.bootstrap.version == 3) {
35490             this.el.removeClass([this.invalidClass, this.validClass]);
35491             this.el.addClass(this.invalidClass);
35492         } else {
35493             this.el.removeClass(['is-invalid','is-valid']);
35494             this.el.addClass(['is-invalid']);
35495         }
35496         
35497         this.fireEvent('invalid', this, msg);
35498         
35499     },
35500     
35501     setValue : function(v, suppressEvent)
35502     {   
35503         if(this.value === v){
35504             return;
35505         }
35506         
35507         this.value = v;
35508         
35509         if(this.rendered){
35510             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35511         }
35512         
35513         Roo.each(this.radioes, function(i){
35514             i.checked = false;
35515             i.el.removeClass('checked');
35516         });
35517         
35518         Roo.each(this.radioes, function(i){
35519             
35520             if(i.value === v || i.value.toString() === v.toString()){
35521                 i.checked = true;
35522                 i.el.addClass('checked');
35523                 
35524                 if(suppressEvent !== true){
35525                     this.fireEvent('check', this, i);
35526                 }
35527                 
35528                 return false;
35529             }
35530             
35531         }, this);
35532         
35533         this.validate();
35534     },
35535     
35536     clearInvalid : function(){
35537         
35538         if(!this.el || this.preventMark){
35539             return;
35540         }
35541         
35542         this.el.removeClass([this.invalidClass]);
35543         
35544         this.fireEvent('valid', this);
35545     }
35546     
35547 });
35548
35549 Roo.apply(Roo.bootstrap.RadioSet, {
35550     
35551     groups: {},
35552     
35553     register : function(set)
35554     {
35555         this.groups[set.name] = set;
35556     },
35557     
35558     get: function(name) 
35559     {
35560         if (typeof(this.groups[name]) == 'undefined') {
35561             return false;
35562         }
35563         
35564         return this.groups[name] ;
35565     }
35566     
35567 });
35568 /*
35569  * Based on:
35570  * Ext JS Library 1.1.1
35571  * Copyright(c) 2006-2007, Ext JS, LLC.
35572  *
35573  * Originally Released Under LGPL - original licence link has changed is not relivant.
35574  *
35575  * Fork - LGPL
35576  * <script type="text/javascript">
35577  */
35578
35579
35580 /**
35581  * @class Roo.bootstrap.SplitBar
35582  * @extends Roo.util.Observable
35583  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35584  * <br><br>
35585  * Usage:
35586  * <pre><code>
35587 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35588                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35589 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35590 split.minSize = 100;
35591 split.maxSize = 600;
35592 split.animate = true;
35593 split.on('moved', splitterMoved);
35594 </code></pre>
35595  * @constructor
35596  * Create a new SplitBar
35597  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35598  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35599  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35600  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35601                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35602                         position of the SplitBar).
35603  */
35604 Roo.bootstrap.SplitBar = function(cfg){
35605     
35606     /** @private */
35607     
35608     //{
35609     //  dragElement : elm
35610     //  resizingElement: el,
35611         // optional..
35612     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35613     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35614         // existingProxy ???
35615     //}
35616     
35617     this.el = Roo.get(cfg.dragElement, true);
35618     this.el.dom.unselectable = "on";
35619     /** @private */
35620     this.resizingEl = Roo.get(cfg.resizingElement, true);
35621
35622     /**
35623      * @private
35624      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35625      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35626      * @type Number
35627      */
35628     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35629     
35630     /**
35631      * The minimum size of the resizing element. (Defaults to 0)
35632      * @type Number
35633      */
35634     this.minSize = 0;
35635     
35636     /**
35637      * The maximum size of the resizing element. (Defaults to 2000)
35638      * @type Number
35639      */
35640     this.maxSize = 2000;
35641     
35642     /**
35643      * Whether to animate the transition to the new size
35644      * @type Boolean
35645      */
35646     this.animate = false;
35647     
35648     /**
35649      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35650      * @type Boolean
35651      */
35652     this.useShim = false;
35653     
35654     /** @private */
35655     this.shim = null;
35656     
35657     if(!cfg.existingProxy){
35658         /** @private */
35659         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35660     }else{
35661         this.proxy = Roo.get(cfg.existingProxy).dom;
35662     }
35663     /** @private */
35664     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35665     
35666     /** @private */
35667     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35668     
35669     /** @private */
35670     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35671     
35672     /** @private */
35673     this.dragSpecs = {};
35674     
35675     /**
35676      * @private The adapter to use to positon and resize elements
35677      */
35678     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35679     this.adapter.init(this);
35680     
35681     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35682         /** @private */
35683         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35684         this.el.addClass("roo-splitbar-h");
35685     }else{
35686         /** @private */
35687         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35688         this.el.addClass("roo-splitbar-v");
35689     }
35690     
35691     this.addEvents({
35692         /**
35693          * @event resize
35694          * Fires when the splitter is moved (alias for {@link #event-moved})
35695          * @param {Roo.bootstrap.SplitBar} this
35696          * @param {Number} newSize the new width or height
35697          */
35698         "resize" : true,
35699         /**
35700          * @event moved
35701          * Fires when the splitter is moved
35702          * @param {Roo.bootstrap.SplitBar} this
35703          * @param {Number} newSize the new width or height
35704          */
35705         "moved" : true,
35706         /**
35707          * @event beforeresize
35708          * Fires before the splitter is dragged
35709          * @param {Roo.bootstrap.SplitBar} this
35710          */
35711         "beforeresize" : true,
35712
35713         "beforeapply" : true
35714     });
35715
35716     Roo.util.Observable.call(this);
35717 };
35718
35719 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35720     onStartProxyDrag : function(x, y){
35721         this.fireEvent("beforeresize", this);
35722         if(!this.overlay){
35723             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35724             o.unselectable();
35725             o.enableDisplayMode("block");
35726             // all splitbars share the same overlay
35727             Roo.bootstrap.SplitBar.prototype.overlay = o;
35728         }
35729         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35730         this.overlay.show();
35731         Roo.get(this.proxy).setDisplayed("block");
35732         var size = this.adapter.getElementSize(this);
35733         this.activeMinSize = this.getMinimumSize();;
35734         this.activeMaxSize = this.getMaximumSize();;
35735         var c1 = size - this.activeMinSize;
35736         var c2 = Math.max(this.activeMaxSize - size, 0);
35737         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35738             this.dd.resetConstraints();
35739             this.dd.setXConstraint(
35740                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35741                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35742             );
35743             this.dd.setYConstraint(0, 0);
35744         }else{
35745             this.dd.resetConstraints();
35746             this.dd.setXConstraint(0, 0);
35747             this.dd.setYConstraint(
35748                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35749                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35750             );
35751          }
35752         this.dragSpecs.startSize = size;
35753         this.dragSpecs.startPoint = [x, y];
35754         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35755     },
35756     
35757     /** 
35758      * @private Called after the drag operation by the DDProxy
35759      */
35760     onEndProxyDrag : function(e){
35761         Roo.get(this.proxy).setDisplayed(false);
35762         var endPoint = Roo.lib.Event.getXY(e);
35763         if(this.overlay){
35764             this.overlay.hide();
35765         }
35766         var newSize;
35767         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35768             newSize = this.dragSpecs.startSize + 
35769                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35770                     endPoint[0] - this.dragSpecs.startPoint[0] :
35771                     this.dragSpecs.startPoint[0] - endPoint[0]
35772                 );
35773         }else{
35774             newSize = this.dragSpecs.startSize + 
35775                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35776                     endPoint[1] - this.dragSpecs.startPoint[1] :
35777                     this.dragSpecs.startPoint[1] - endPoint[1]
35778                 );
35779         }
35780         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35781         if(newSize != this.dragSpecs.startSize){
35782             if(this.fireEvent('beforeapply', this, newSize) !== false){
35783                 this.adapter.setElementSize(this, newSize);
35784                 this.fireEvent("moved", this, newSize);
35785                 this.fireEvent("resize", this, newSize);
35786             }
35787         }
35788     },
35789     
35790     /**
35791      * Get the adapter this SplitBar uses
35792      * @return The adapter object
35793      */
35794     getAdapter : function(){
35795         return this.adapter;
35796     },
35797     
35798     /**
35799      * Set the adapter this SplitBar uses
35800      * @param {Object} adapter A SplitBar adapter object
35801      */
35802     setAdapter : function(adapter){
35803         this.adapter = adapter;
35804         this.adapter.init(this);
35805     },
35806     
35807     /**
35808      * Gets the minimum size for the resizing element
35809      * @return {Number} The minimum size
35810      */
35811     getMinimumSize : function(){
35812         return this.minSize;
35813     },
35814     
35815     /**
35816      * Sets the minimum size for the resizing element
35817      * @param {Number} minSize The minimum size
35818      */
35819     setMinimumSize : function(minSize){
35820         this.minSize = minSize;
35821     },
35822     
35823     /**
35824      * Gets the maximum size for the resizing element
35825      * @return {Number} The maximum size
35826      */
35827     getMaximumSize : function(){
35828         return this.maxSize;
35829     },
35830     
35831     /**
35832      * Sets the maximum size for the resizing element
35833      * @param {Number} maxSize The maximum size
35834      */
35835     setMaximumSize : function(maxSize){
35836         this.maxSize = maxSize;
35837     },
35838     
35839     /**
35840      * Sets the initialize size for the resizing element
35841      * @param {Number} size The initial size
35842      */
35843     setCurrentSize : function(size){
35844         var oldAnimate = this.animate;
35845         this.animate = false;
35846         this.adapter.setElementSize(this, size);
35847         this.animate = oldAnimate;
35848     },
35849     
35850     /**
35851      * Destroy this splitbar. 
35852      * @param {Boolean} removeEl True to remove the element
35853      */
35854     destroy : function(removeEl){
35855         if(this.shim){
35856             this.shim.remove();
35857         }
35858         this.dd.unreg();
35859         this.proxy.parentNode.removeChild(this.proxy);
35860         if(removeEl){
35861             this.el.remove();
35862         }
35863     }
35864 });
35865
35866 /**
35867  * @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.
35868  */
35869 Roo.bootstrap.SplitBar.createProxy = function(dir){
35870     var proxy = new Roo.Element(document.createElement("div"));
35871     proxy.unselectable();
35872     var cls = 'roo-splitbar-proxy';
35873     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35874     document.body.appendChild(proxy.dom);
35875     return proxy.dom;
35876 };
35877
35878 /** 
35879  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35880  * Default Adapter. It assumes the splitter and resizing element are not positioned
35881  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35882  */
35883 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35884 };
35885
35886 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35887     // do nothing for now
35888     init : function(s){
35889     
35890     },
35891     /**
35892      * Called before drag operations to get the current size of the resizing element. 
35893      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35894      */
35895      getElementSize : function(s){
35896         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35897             return s.resizingEl.getWidth();
35898         }else{
35899             return s.resizingEl.getHeight();
35900         }
35901     },
35902     
35903     /**
35904      * Called after drag operations to set the size of the resizing element.
35905      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35906      * @param {Number} newSize The new size to set
35907      * @param {Function} onComplete A function to be invoked when resizing is complete
35908      */
35909     setElementSize : function(s, newSize, onComplete){
35910         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35911             if(!s.animate){
35912                 s.resizingEl.setWidth(newSize);
35913                 if(onComplete){
35914                     onComplete(s, newSize);
35915                 }
35916             }else{
35917                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35918             }
35919         }else{
35920             
35921             if(!s.animate){
35922                 s.resizingEl.setHeight(newSize);
35923                 if(onComplete){
35924                     onComplete(s, newSize);
35925                 }
35926             }else{
35927                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35928             }
35929         }
35930     }
35931 };
35932
35933 /** 
35934  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35935  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35936  * Adapter that  moves the splitter element to align with the resized sizing element. 
35937  * Used with an absolute positioned SplitBar.
35938  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35939  * document.body, make sure you assign an id to the body element.
35940  */
35941 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35942     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35943     this.container = Roo.get(container);
35944 };
35945
35946 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35947     init : function(s){
35948         this.basic.init(s);
35949     },
35950     
35951     getElementSize : function(s){
35952         return this.basic.getElementSize(s);
35953     },
35954     
35955     setElementSize : function(s, newSize, onComplete){
35956         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35957     },
35958     
35959     moveSplitter : function(s){
35960         var yes = Roo.bootstrap.SplitBar;
35961         switch(s.placement){
35962             case yes.LEFT:
35963                 s.el.setX(s.resizingEl.getRight());
35964                 break;
35965             case yes.RIGHT:
35966                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35967                 break;
35968             case yes.TOP:
35969                 s.el.setY(s.resizingEl.getBottom());
35970                 break;
35971             case yes.BOTTOM:
35972                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35973                 break;
35974         }
35975     }
35976 };
35977
35978 /**
35979  * Orientation constant - Create a vertical SplitBar
35980  * @static
35981  * @type Number
35982  */
35983 Roo.bootstrap.SplitBar.VERTICAL = 1;
35984
35985 /**
35986  * Orientation constant - Create a horizontal SplitBar
35987  * @static
35988  * @type Number
35989  */
35990 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35991
35992 /**
35993  * Placement constant - The resizing element is to the left of the splitter element
35994  * @static
35995  * @type Number
35996  */
35997 Roo.bootstrap.SplitBar.LEFT = 1;
35998
35999 /**
36000  * Placement constant - The resizing element is to the right of the splitter element
36001  * @static
36002  * @type Number
36003  */
36004 Roo.bootstrap.SplitBar.RIGHT = 2;
36005
36006 /**
36007  * Placement constant - The resizing element is positioned above the splitter element
36008  * @static
36009  * @type Number
36010  */
36011 Roo.bootstrap.SplitBar.TOP = 3;
36012
36013 /**
36014  * Placement constant - The resizing element is positioned under splitter element
36015  * @static
36016  * @type Number
36017  */
36018 Roo.bootstrap.SplitBar.BOTTOM = 4;
36019 Roo.namespace("Roo.bootstrap.layout");/*
36020  * Based on:
36021  * Ext JS Library 1.1.1
36022  * Copyright(c) 2006-2007, Ext JS, LLC.
36023  *
36024  * Originally Released Under LGPL - original licence link has changed is not relivant.
36025  *
36026  * Fork - LGPL
36027  * <script type="text/javascript">
36028  */
36029
36030 /**
36031  * @class Roo.bootstrap.layout.Manager
36032  * @extends Roo.bootstrap.Component
36033  * Base class for layout managers.
36034  */
36035 Roo.bootstrap.layout.Manager = function(config)
36036 {
36037     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36038
36039
36040
36041
36042
36043     /** false to disable window resize monitoring @type Boolean */
36044     this.monitorWindowResize = true;
36045     this.regions = {};
36046     this.addEvents({
36047         /**
36048          * @event layout
36049          * Fires when a layout is performed.
36050          * @param {Roo.LayoutManager} this
36051          */
36052         "layout" : true,
36053         /**
36054          * @event regionresized
36055          * Fires when the user resizes a region.
36056          * @param {Roo.LayoutRegion} region The resized region
36057          * @param {Number} newSize The new size (width for east/west, height for north/south)
36058          */
36059         "regionresized" : true,
36060         /**
36061          * @event regioncollapsed
36062          * Fires when a region is collapsed.
36063          * @param {Roo.LayoutRegion} region The collapsed region
36064          */
36065         "regioncollapsed" : true,
36066         /**
36067          * @event regionexpanded
36068          * Fires when a region is expanded.
36069          * @param {Roo.LayoutRegion} region The expanded region
36070          */
36071         "regionexpanded" : true
36072     });
36073     this.updating = false;
36074
36075     if (config.el) {
36076         this.el = Roo.get(config.el);
36077         this.initEvents();
36078     }
36079
36080 };
36081
36082 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36083
36084
36085     regions : null,
36086
36087     monitorWindowResize : true,
36088
36089
36090     updating : false,
36091
36092
36093     onRender : function(ct, position)
36094     {
36095         if(!this.el){
36096             this.el = Roo.get(ct);
36097             this.initEvents();
36098         }
36099         //this.fireEvent('render',this);
36100     },
36101
36102
36103     initEvents: function()
36104     {
36105
36106
36107         // ie scrollbar fix
36108         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36109             document.body.scroll = "no";
36110         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36111             this.el.position('relative');
36112         }
36113         this.id = this.el.id;
36114         this.el.addClass("roo-layout-container");
36115         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36116         if(this.el.dom != document.body ) {
36117             this.el.on('resize', this.layout,this);
36118             this.el.on('show', this.layout,this);
36119         }
36120
36121     },
36122
36123     /**
36124      * Returns true if this layout is currently being updated
36125      * @return {Boolean}
36126      */
36127     isUpdating : function(){
36128         return this.updating;
36129     },
36130
36131     /**
36132      * Suspend the LayoutManager from doing auto-layouts while
36133      * making multiple add or remove calls
36134      */
36135     beginUpdate : function(){
36136         this.updating = true;
36137     },
36138
36139     /**
36140      * Restore auto-layouts and optionally disable the manager from performing a layout
36141      * @param {Boolean} noLayout true to disable a layout update
36142      */
36143     endUpdate : function(noLayout){
36144         this.updating = false;
36145         if(!noLayout){
36146             this.layout();
36147         }
36148     },
36149
36150     layout: function(){
36151         // abstract...
36152     },
36153
36154     onRegionResized : function(region, newSize){
36155         this.fireEvent("regionresized", region, newSize);
36156         this.layout();
36157     },
36158
36159     onRegionCollapsed : function(region){
36160         this.fireEvent("regioncollapsed", region);
36161     },
36162
36163     onRegionExpanded : function(region){
36164         this.fireEvent("regionexpanded", region);
36165     },
36166
36167     /**
36168      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36169      * performs box-model adjustments.
36170      * @return {Object} The size as an object {width: (the width), height: (the height)}
36171      */
36172     getViewSize : function()
36173     {
36174         var size;
36175         if(this.el.dom != document.body){
36176             size = this.el.getSize();
36177         }else{
36178             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36179         }
36180         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36181         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36182         return size;
36183     },
36184
36185     /**
36186      * Returns the Element this layout is bound to.
36187      * @return {Roo.Element}
36188      */
36189     getEl : function(){
36190         return this.el;
36191     },
36192
36193     /**
36194      * Returns the specified region.
36195      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36196      * @return {Roo.LayoutRegion}
36197      */
36198     getRegion : function(target){
36199         return this.regions[target.toLowerCase()];
36200     },
36201
36202     onWindowResize : function(){
36203         if(this.monitorWindowResize){
36204             this.layout();
36205         }
36206     }
36207 });
36208 /*
36209  * Based on:
36210  * Ext JS Library 1.1.1
36211  * Copyright(c) 2006-2007, Ext JS, LLC.
36212  *
36213  * Originally Released Under LGPL - original licence link has changed is not relivant.
36214  *
36215  * Fork - LGPL
36216  * <script type="text/javascript">
36217  */
36218 /**
36219  * @class Roo.bootstrap.layout.Border
36220  * @extends Roo.bootstrap.layout.Manager
36221  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36222  * please see: examples/bootstrap/nested.html<br><br>
36223  
36224 <b>The container the layout is rendered into can be either the body element or any other element.
36225 If it is not the body element, the container needs to either be an absolute positioned element,
36226 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36227 the container size if it is not the body element.</b>
36228
36229 * @constructor
36230 * Create a new Border
36231 * @param {Object} config Configuration options
36232  */
36233 Roo.bootstrap.layout.Border = function(config){
36234     config = config || {};
36235     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36236     
36237     
36238     
36239     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36240         if(config[region]){
36241             config[region].region = region;
36242             this.addRegion(config[region]);
36243         }
36244     },this);
36245     
36246 };
36247
36248 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36249
36250 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36251     
36252     parent : false, // this might point to a 'nest' or a ???
36253     
36254     /**
36255      * Creates and adds a new region if it doesn't already exist.
36256      * @param {String} target The target region key (north, south, east, west or center).
36257      * @param {Object} config The regions config object
36258      * @return {BorderLayoutRegion} The new region
36259      */
36260     addRegion : function(config)
36261     {
36262         if(!this.regions[config.region]){
36263             var r = this.factory(config);
36264             this.bindRegion(r);
36265         }
36266         return this.regions[config.region];
36267     },
36268
36269     // private (kinda)
36270     bindRegion : function(r){
36271         this.regions[r.config.region] = r;
36272         
36273         r.on("visibilitychange",    this.layout, this);
36274         r.on("paneladded",          this.layout, this);
36275         r.on("panelremoved",        this.layout, this);
36276         r.on("invalidated",         this.layout, this);
36277         r.on("resized",             this.onRegionResized, this);
36278         r.on("collapsed",           this.onRegionCollapsed, this);
36279         r.on("expanded",            this.onRegionExpanded, this);
36280     },
36281
36282     /**
36283      * Performs a layout update.
36284      */
36285     layout : function()
36286     {
36287         if(this.updating) {
36288             return;
36289         }
36290         
36291         // render all the rebions if they have not been done alreayd?
36292         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36293             if(this.regions[region] && !this.regions[region].bodyEl){
36294                 this.regions[region].onRender(this.el)
36295             }
36296         },this);
36297         
36298         var size = this.getViewSize();
36299         var w = size.width;
36300         var h = size.height;
36301         var centerW = w;
36302         var centerH = h;
36303         var centerY = 0;
36304         var centerX = 0;
36305         //var x = 0, y = 0;
36306
36307         var rs = this.regions;
36308         var north = rs["north"];
36309         var south = rs["south"]; 
36310         var west = rs["west"];
36311         var east = rs["east"];
36312         var center = rs["center"];
36313         //if(this.hideOnLayout){ // not supported anymore
36314             //c.el.setStyle("display", "none");
36315         //}
36316         if(north && north.isVisible()){
36317             var b = north.getBox();
36318             var m = north.getMargins();
36319             b.width = w - (m.left+m.right);
36320             b.x = m.left;
36321             b.y = m.top;
36322             centerY = b.height + b.y + m.bottom;
36323             centerH -= centerY;
36324             north.updateBox(this.safeBox(b));
36325         }
36326         if(south && south.isVisible()){
36327             var b = south.getBox();
36328             var m = south.getMargins();
36329             b.width = w - (m.left+m.right);
36330             b.x = m.left;
36331             var totalHeight = (b.height + m.top + m.bottom);
36332             b.y = h - totalHeight + m.top;
36333             centerH -= totalHeight;
36334             south.updateBox(this.safeBox(b));
36335         }
36336         if(west && west.isVisible()){
36337             var b = west.getBox();
36338             var m = west.getMargins();
36339             b.height = centerH - (m.top+m.bottom);
36340             b.x = m.left;
36341             b.y = centerY + m.top;
36342             var totalWidth = (b.width + m.left + m.right);
36343             centerX += totalWidth;
36344             centerW -= totalWidth;
36345             west.updateBox(this.safeBox(b));
36346         }
36347         if(east && east.isVisible()){
36348             var b = east.getBox();
36349             var m = east.getMargins();
36350             b.height = centerH - (m.top+m.bottom);
36351             var totalWidth = (b.width + m.left + m.right);
36352             b.x = w - totalWidth + m.left;
36353             b.y = centerY + m.top;
36354             centerW -= totalWidth;
36355             east.updateBox(this.safeBox(b));
36356         }
36357         if(center){
36358             var m = center.getMargins();
36359             var centerBox = {
36360                 x: centerX + m.left,
36361                 y: centerY + m.top,
36362                 width: centerW - (m.left+m.right),
36363                 height: centerH - (m.top+m.bottom)
36364             };
36365             //if(this.hideOnLayout){
36366                 //center.el.setStyle("display", "block");
36367             //}
36368             center.updateBox(this.safeBox(centerBox));
36369         }
36370         this.el.repaint();
36371         this.fireEvent("layout", this);
36372     },
36373
36374     // private
36375     safeBox : function(box){
36376         box.width = Math.max(0, box.width);
36377         box.height = Math.max(0, box.height);
36378         return box;
36379     },
36380
36381     /**
36382      * Adds a ContentPanel (or subclass) to this layout.
36383      * @param {String} target The target region key (north, south, east, west or center).
36384      * @param {Roo.ContentPanel} panel The panel to add
36385      * @return {Roo.ContentPanel} The added panel
36386      */
36387     add : function(target, panel){
36388          
36389         target = target.toLowerCase();
36390         return this.regions[target].add(panel);
36391     },
36392
36393     /**
36394      * Remove a ContentPanel (or subclass) to this layout.
36395      * @param {String} target The target region key (north, south, east, west or center).
36396      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36397      * @return {Roo.ContentPanel} The removed panel
36398      */
36399     remove : function(target, panel){
36400         target = target.toLowerCase();
36401         return this.regions[target].remove(panel);
36402     },
36403
36404     /**
36405      * Searches all regions for a panel with the specified id
36406      * @param {String} panelId
36407      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36408      */
36409     findPanel : function(panelId){
36410         var rs = this.regions;
36411         for(var target in rs){
36412             if(typeof rs[target] != "function"){
36413                 var p = rs[target].getPanel(panelId);
36414                 if(p){
36415                     return p;
36416                 }
36417             }
36418         }
36419         return null;
36420     },
36421
36422     /**
36423      * Searches all regions for a panel with the specified id and activates (shows) it.
36424      * @param {String/ContentPanel} panelId The panels id or the panel itself
36425      * @return {Roo.ContentPanel} The shown panel or null
36426      */
36427     showPanel : function(panelId) {
36428       var rs = this.regions;
36429       for(var target in rs){
36430          var r = rs[target];
36431          if(typeof r != "function"){
36432             if(r.hasPanel(panelId)){
36433                return r.showPanel(panelId);
36434             }
36435          }
36436       }
36437       return null;
36438    },
36439
36440    /**
36441      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36442      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36443      */
36444    /*
36445     restoreState : function(provider){
36446         if(!provider){
36447             provider = Roo.state.Manager;
36448         }
36449         var sm = new Roo.LayoutStateManager();
36450         sm.init(this, provider);
36451     },
36452 */
36453  
36454  
36455     /**
36456      * Adds a xtype elements to the layout.
36457      * <pre><code>
36458
36459 layout.addxtype({
36460        xtype : 'ContentPanel',
36461        region: 'west',
36462        items: [ .... ]
36463    }
36464 );
36465
36466 layout.addxtype({
36467         xtype : 'NestedLayoutPanel',
36468         region: 'west',
36469         layout: {
36470            center: { },
36471            west: { }   
36472         },
36473         items : [ ... list of content panels or nested layout panels.. ]
36474    }
36475 );
36476 </code></pre>
36477      * @param {Object} cfg Xtype definition of item to add.
36478      */
36479     addxtype : function(cfg)
36480     {
36481         // basically accepts a pannel...
36482         // can accept a layout region..!?!?
36483         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36484         
36485         
36486         // theory?  children can only be panels??
36487         
36488         //if (!cfg.xtype.match(/Panel$/)) {
36489         //    return false;
36490         //}
36491         var ret = false;
36492         
36493         if (typeof(cfg.region) == 'undefined') {
36494             Roo.log("Failed to add Panel, region was not set");
36495             Roo.log(cfg);
36496             return false;
36497         }
36498         var region = cfg.region;
36499         delete cfg.region;
36500         
36501           
36502         var xitems = [];
36503         if (cfg.items) {
36504             xitems = cfg.items;
36505             delete cfg.items;
36506         }
36507         var nb = false;
36508         
36509         if ( region == 'center') {
36510             Roo.log("Center: " + cfg.title);
36511         }
36512         
36513         
36514         switch(cfg.xtype) 
36515         {
36516             case 'Content':  // ContentPanel (el, cfg)
36517             case 'Scroll':  // ContentPanel (el, cfg)
36518             case 'View': 
36519                 cfg.autoCreate = cfg.autoCreate || true;
36520                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36521                 //} else {
36522                 //    var el = this.el.createChild();
36523                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36524                 //}
36525                 
36526                 this.add(region, ret);
36527                 break;
36528             
36529             /*
36530             case 'TreePanel': // our new panel!
36531                 cfg.el = this.el.createChild();
36532                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36533                 this.add(region, ret);
36534                 break;
36535             */
36536             
36537             case 'Nest': 
36538                 // create a new Layout (which is  a Border Layout...
36539                 
36540                 var clayout = cfg.layout;
36541                 clayout.el  = this.el.createChild();
36542                 clayout.items   = clayout.items  || [];
36543                 
36544                 delete cfg.layout;
36545                 
36546                 // replace this exitems with the clayout ones..
36547                 xitems = clayout.items;
36548                  
36549                 // force background off if it's in center...
36550                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36551                     cfg.background = false;
36552                 }
36553                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36554                 
36555                 
36556                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36557                 //console.log('adding nested layout panel '  + cfg.toSource());
36558                 this.add(region, ret);
36559                 nb = {}; /// find first...
36560                 break;
36561             
36562             case 'Grid':
36563                 
36564                 // needs grid and region
36565                 
36566                 //var el = this.getRegion(region).el.createChild();
36567                 /*
36568                  *var el = this.el.createChild();
36569                 // create the grid first...
36570                 cfg.grid.container = el;
36571                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36572                 */
36573                 
36574                 if (region == 'center' && this.active ) {
36575                     cfg.background = false;
36576                 }
36577                 
36578                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36579                 
36580                 this.add(region, ret);
36581                 /*
36582                 if (cfg.background) {
36583                     // render grid on panel activation (if panel background)
36584                     ret.on('activate', function(gp) {
36585                         if (!gp.grid.rendered) {
36586                     //        gp.grid.render(el);
36587                         }
36588                     });
36589                 } else {
36590                   //  cfg.grid.render(el);
36591                 }
36592                 */
36593                 break;
36594            
36595            
36596             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36597                 // it was the old xcomponent building that caused this before.
36598                 // espeically if border is the top element in the tree.
36599                 ret = this;
36600                 break; 
36601                 
36602                     
36603                 
36604                 
36605                 
36606             default:
36607                 /*
36608                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36609                     
36610                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36611                     this.add(region, ret);
36612                 } else {
36613                 */
36614                     Roo.log(cfg);
36615                     throw "Can not add '" + cfg.xtype + "' to Border";
36616                     return null;
36617              
36618                                 
36619              
36620         }
36621         this.beginUpdate();
36622         // add children..
36623         var region = '';
36624         var abn = {};
36625         Roo.each(xitems, function(i)  {
36626             region = nb && i.region ? i.region : false;
36627             
36628             var add = ret.addxtype(i);
36629            
36630             if (region) {
36631                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36632                 if (!i.background) {
36633                     abn[region] = nb[region] ;
36634                 }
36635             }
36636             
36637         });
36638         this.endUpdate();
36639
36640         // make the last non-background panel active..
36641         //if (nb) { Roo.log(abn); }
36642         if (nb) {
36643             
36644             for(var r in abn) {
36645                 region = this.getRegion(r);
36646                 if (region) {
36647                     // tried using nb[r], but it does not work..
36648                      
36649                     region.showPanel(abn[r]);
36650                    
36651                 }
36652             }
36653         }
36654         return ret;
36655         
36656     },
36657     
36658     
36659 // private
36660     factory : function(cfg)
36661     {
36662         
36663         var validRegions = Roo.bootstrap.layout.Border.regions;
36664
36665         var target = cfg.region;
36666         cfg.mgr = this;
36667         
36668         var r = Roo.bootstrap.layout;
36669         Roo.log(target);
36670         switch(target){
36671             case "north":
36672                 return new r.North(cfg);
36673             case "south":
36674                 return new r.South(cfg);
36675             case "east":
36676                 return new r.East(cfg);
36677             case "west":
36678                 return new r.West(cfg);
36679             case "center":
36680                 return new r.Center(cfg);
36681         }
36682         throw 'Layout region "'+target+'" not supported.';
36683     }
36684     
36685     
36686 });
36687  /*
36688  * Based on:
36689  * Ext JS Library 1.1.1
36690  * Copyright(c) 2006-2007, Ext JS, LLC.
36691  *
36692  * Originally Released Under LGPL - original licence link has changed is not relivant.
36693  *
36694  * Fork - LGPL
36695  * <script type="text/javascript">
36696  */
36697  
36698 /**
36699  * @class Roo.bootstrap.layout.Basic
36700  * @extends Roo.util.Observable
36701  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36702  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36703  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36704  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36705  * @cfg {string}   region  the region that it inhabits..
36706  * @cfg {bool}   skipConfig skip config?
36707  * 
36708
36709  */
36710 Roo.bootstrap.layout.Basic = function(config){
36711     
36712     this.mgr = config.mgr;
36713     
36714     this.position = config.region;
36715     
36716     var skipConfig = config.skipConfig;
36717     
36718     this.events = {
36719         /**
36720          * @scope Roo.BasicLayoutRegion
36721          */
36722         
36723         /**
36724          * @event beforeremove
36725          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36726          * @param {Roo.LayoutRegion} this
36727          * @param {Roo.ContentPanel} panel The panel
36728          * @param {Object} e The cancel event object
36729          */
36730         "beforeremove" : true,
36731         /**
36732          * @event invalidated
36733          * Fires when the layout for this region is changed.
36734          * @param {Roo.LayoutRegion} this
36735          */
36736         "invalidated" : true,
36737         /**
36738          * @event visibilitychange
36739          * Fires when this region is shown or hidden 
36740          * @param {Roo.LayoutRegion} this
36741          * @param {Boolean} visibility true or false
36742          */
36743         "visibilitychange" : true,
36744         /**
36745          * @event paneladded
36746          * Fires when a panel is added. 
36747          * @param {Roo.LayoutRegion} this
36748          * @param {Roo.ContentPanel} panel The panel
36749          */
36750         "paneladded" : true,
36751         /**
36752          * @event panelremoved
36753          * Fires when a panel is removed. 
36754          * @param {Roo.LayoutRegion} this
36755          * @param {Roo.ContentPanel} panel The panel
36756          */
36757         "panelremoved" : true,
36758         /**
36759          * @event beforecollapse
36760          * Fires when this region before collapse.
36761          * @param {Roo.LayoutRegion} this
36762          */
36763         "beforecollapse" : true,
36764         /**
36765          * @event collapsed
36766          * Fires when this region is collapsed.
36767          * @param {Roo.LayoutRegion} this
36768          */
36769         "collapsed" : true,
36770         /**
36771          * @event expanded
36772          * Fires when this region is expanded.
36773          * @param {Roo.LayoutRegion} this
36774          */
36775         "expanded" : true,
36776         /**
36777          * @event slideshow
36778          * Fires when this region is slid into view.
36779          * @param {Roo.LayoutRegion} this
36780          */
36781         "slideshow" : true,
36782         /**
36783          * @event slidehide
36784          * Fires when this region slides out of view. 
36785          * @param {Roo.LayoutRegion} this
36786          */
36787         "slidehide" : true,
36788         /**
36789          * @event panelactivated
36790          * Fires when a panel is activated. 
36791          * @param {Roo.LayoutRegion} this
36792          * @param {Roo.ContentPanel} panel The activated panel
36793          */
36794         "panelactivated" : true,
36795         /**
36796          * @event resized
36797          * Fires when the user resizes this region. 
36798          * @param {Roo.LayoutRegion} this
36799          * @param {Number} newSize The new size (width for east/west, height for north/south)
36800          */
36801         "resized" : true
36802     };
36803     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36804     this.panels = new Roo.util.MixedCollection();
36805     this.panels.getKey = this.getPanelId.createDelegate(this);
36806     this.box = null;
36807     this.activePanel = null;
36808     // ensure listeners are added...
36809     
36810     if (config.listeners || config.events) {
36811         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36812             listeners : config.listeners || {},
36813             events : config.events || {}
36814         });
36815     }
36816     
36817     if(skipConfig !== true){
36818         this.applyConfig(config);
36819     }
36820 };
36821
36822 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36823 {
36824     getPanelId : function(p){
36825         return p.getId();
36826     },
36827     
36828     applyConfig : function(config){
36829         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36830         this.config = config;
36831         
36832     },
36833     
36834     /**
36835      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36836      * the width, for horizontal (north, south) the height.
36837      * @param {Number} newSize The new width or height
36838      */
36839     resizeTo : function(newSize){
36840         var el = this.el ? this.el :
36841                  (this.activePanel ? this.activePanel.getEl() : null);
36842         if(el){
36843             switch(this.position){
36844                 case "east":
36845                 case "west":
36846                     el.setWidth(newSize);
36847                     this.fireEvent("resized", this, newSize);
36848                 break;
36849                 case "north":
36850                 case "south":
36851                     el.setHeight(newSize);
36852                     this.fireEvent("resized", this, newSize);
36853                 break;                
36854             }
36855         }
36856     },
36857     
36858     getBox : function(){
36859         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36860     },
36861     
36862     getMargins : function(){
36863         return this.margins;
36864     },
36865     
36866     updateBox : function(box){
36867         this.box = box;
36868         var el = this.activePanel.getEl();
36869         el.dom.style.left = box.x + "px";
36870         el.dom.style.top = box.y + "px";
36871         this.activePanel.setSize(box.width, box.height);
36872     },
36873     
36874     /**
36875      * Returns the container element for this region.
36876      * @return {Roo.Element}
36877      */
36878     getEl : function(){
36879         return this.activePanel;
36880     },
36881     
36882     /**
36883      * Returns true if this region is currently visible.
36884      * @return {Boolean}
36885      */
36886     isVisible : function(){
36887         return this.activePanel ? true : false;
36888     },
36889     
36890     setActivePanel : function(panel){
36891         panel = this.getPanel(panel);
36892         if(this.activePanel && this.activePanel != panel){
36893             this.activePanel.setActiveState(false);
36894             this.activePanel.getEl().setLeftTop(-10000,-10000);
36895         }
36896         this.activePanel = panel;
36897         panel.setActiveState(true);
36898         if(this.box){
36899             panel.setSize(this.box.width, this.box.height);
36900         }
36901         this.fireEvent("panelactivated", this, panel);
36902         this.fireEvent("invalidated");
36903     },
36904     
36905     /**
36906      * Show the specified panel.
36907      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36908      * @return {Roo.ContentPanel} The shown panel or null
36909      */
36910     showPanel : function(panel){
36911         panel = this.getPanel(panel);
36912         if(panel){
36913             this.setActivePanel(panel);
36914         }
36915         return panel;
36916     },
36917     
36918     /**
36919      * Get the active panel for this region.
36920      * @return {Roo.ContentPanel} The active panel or null
36921      */
36922     getActivePanel : function(){
36923         return this.activePanel;
36924     },
36925     
36926     /**
36927      * Add the passed ContentPanel(s)
36928      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36929      * @return {Roo.ContentPanel} The panel added (if only one was added)
36930      */
36931     add : function(panel){
36932         if(arguments.length > 1){
36933             for(var i = 0, len = arguments.length; i < len; i++) {
36934                 this.add(arguments[i]);
36935             }
36936             return null;
36937         }
36938         if(this.hasPanel(panel)){
36939             this.showPanel(panel);
36940             return panel;
36941         }
36942         var el = panel.getEl();
36943         if(el.dom.parentNode != this.mgr.el.dom){
36944             this.mgr.el.dom.appendChild(el.dom);
36945         }
36946         if(panel.setRegion){
36947             panel.setRegion(this);
36948         }
36949         this.panels.add(panel);
36950         el.setStyle("position", "absolute");
36951         if(!panel.background){
36952             this.setActivePanel(panel);
36953             if(this.config.initialSize && this.panels.getCount()==1){
36954                 this.resizeTo(this.config.initialSize);
36955             }
36956         }
36957         this.fireEvent("paneladded", this, panel);
36958         return panel;
36959     },
36960     
36961     /**
36962      * Returns true if the panel is in this region.
36963      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36964      * @return {Boolean}
36965      */
36966     hasPanel : function(panel){
36967         if(typeof panel == "object"){ // must be panel obj
36968             panel = panel.getId();
36969         }
36970         return this.getPanel(panel) ? true : false;
36971     },
36972     
36973     /**
36974      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36975      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36976      * @param {Boolean} preservePanel Overrides the config preservePanel option
36977      * @return {Roo.ContentPanel} The panel that was removed
36978      */
36979     remove : function(panel, preservePanel){
36980         panel = this.getPanel(panel);
36981         if(!panel){
36982             return null;
36983         }
36984         var e = {};
36985         this.fireEvent("beforeremove", this, panel, e);
36986         if(e.cancel === true){
36987             return null;
36988         }
36989         var panelId = panel.getId();
36990         this.panels.removeKey(panelId);
36991         return panel;
36992     },
36993     
36994     /**
36995      * Returns the panel specified or null if it's not in this region.
36996      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36997      * @return {Roo.ContentPanel}
36998      */
36999     getPanel : function(id){
37000         if(typeof id == "object"){ // must be panel obj
37001             return id;
37002         }
37003         return this.panels.get(id);
37004     },
37005     
37006     /**
37007      * Returns this regions position (north/south/east/west/center).
37008      * @return {String} 
37009      */
37010     getPosition: function(){
37011         return this.position;    
37012     }
37013 });/*
37014  * Based on:
37015  * Ext JS Library 1.1.1
37016  * Copyright(c) 2006-2007, Ext JS, LLC.
37017  *
37018  * Originally Released Under LGPL - original licence link has changed is not relivant.
37019  *
37020  * Fork - LGPL
37021  * <script type="text/javascript">
37022  */
37023  
37024 /**
37025  * @class Roo.bootstrap.layout.Region
37026  * @extends Roo.bootstrap.layout.Basic
37027  * This class represents a region in a layout manager.
37028  
37029  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37030  * @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})
37031  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37032  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37033  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37034  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37035  * @cfg {String}    title           The title for the region (overrides panel titles)
37036  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37037  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37038  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37039  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37040  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37041  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37042  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37043  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37044  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37045  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37046
37047  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37048  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37049  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37050  * @cfg {Number}    width           For East/West panels
37051  * @cfg {Number}    height          For North/South panels
37052  * @cfg {Boolean}   split           To show the splitter
37053  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37054  * 
37055  * @cfg {string}   cls             Extra CSS classes to add to region
37056  * 
37057  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37058  * @cfg {string}   region  the region that it inhabits..
37059  *
37060
37061  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37062  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37063
37064  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37065  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37066  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37067  */
37068 Roo.bootstrap.layout.Region = function(config)
37069 {
37070     this.applyConfig(config);
37071
37072     var mgr = config.mgr;
37073     var pos = config.region;
37074     config.skipConfig = true;
37075     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37076     
37077     if (mgr.el) {
37078         this.onRender(mgr.el);   
37079     }
37080      
37081     this.visible = true;
37082     this.collapsed = false;
37083     this.unrendered_panels = [];
37084 };
37085
37086 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37087
37088     position: '', // set by wrapper (eg. north/south etc..)
37089     unrendered_panels : null,  // unrendered panels.
37090     
37091     tabPosition : false,
37092     
37093     mgr: false, // points to 'Border'
37094     
37095     
37096     createBody : function(){
37097         /** This region's body element 
37098         * @type Roo.Element */
37099         this.bodyEl = this.el.createChild({
37100                 tag: "div",
37101                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37102         });
37103     },
37104
37105     onRender: function(ctr, pos)
37106     {
37107         var dh = Roo.DomHelper;
37108         /** This region's container element 
37109         * @type Roo.Element */
37110         this.el = dh.append(ctr.dom, {
37111                 tag: "div",
37112                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37113             }, true);
37114         /** This region's title element 
37115         * @type Roo.Element */
37116     
37117         this.titleEl = dh.append(this.el.dom,  {
37118                 tag: "div",
37119                 unselectable: "on",
37120                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37121                 children:[
37122                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37123                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37124                 ]
37125             }, true);
37126         
37127         this.titleEl.enableDisplayMode();
37128         /** This region's title text element 
37129         * @type HTMLElement */
37130         this.titleTextEl = this.titleEl.dom.firstChild;
37131         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37132         /*
37133         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37134         this.closeBtn.enableDisplayMode();
37135         this.closeBtn.on("click", this.closeClicked, this);
37136         this.closeBtn.hide();
37137     */
37138         this.createBody(this.config);
37139         if(this.config.hideWhenEmpty){
37140             this.hide();
37141             this.on("paneladded", this.validateVisibility, this);
37142             this.on("panelremoved", this.validateVisibility, this);
37143         }
37144         if(this.autoScroll){
37145             this.bodyEl.setStyle("overflow", "auto");
37146         }else{
37147             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37148         }
37149         //if(c.titlebar !== false){
37150             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37151                 this.titleEl.hide();
37152             }else{
37153                 this.titleEl.show();
37154                 if(this.config.title){
37155                     this.titleTextEl.innerHTML = this.config.title;
37156                 }
37157             }
37158         //}
37159         if(this.config.collapsed){
37160             this.collapse(true);
37161         }
37162         if(this.config.hidden){
37163             this.hide();
37164         }
37165         
37166         if (this.unrendered_panels && this.unrendered_panels.length) {
37167             for (var i =0;i< this.unrendered_panels.length; i++) {
37168                 this.add(this.unrendered_panels[i]);
37169             }
37170             this.unrendered_panels = null;
37171             
37172         }
37173         
37174     },
37175     
37176     applyConfig : function(c)
37177     {
37178         /*
37179          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37180             var dh = Roo.DomHelper;
37181             if(c.titlebar !== false){
37182                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37183                 this.collapseBtn.on("click", this.collapse, this);
37184                 this.collapseBtn.enableDisplayMode();
37185                 /*
37186                 if(c.showPin === true || this.showPin){
37187                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37188                     this.stickBtn.enableDisplayMode();
37189                     this.stickBtn.on("click", this.expand, this);
37190                     this.stickBtn.hide();
37191                 }
37192                 
37193             }
37194             */
37195             /** This region's collapsed element
37196             * @type Roo.Element */
37197             /*
37198              *
37199             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37200                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37201             ]}, true);
37202             
37203             if(c.floatable !== false){
37204                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37205                this.collapsedEl.on("click", this.collapseClick, this);
37206             }
37207
37208             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37209                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37210                    id: "message", unselectable: "on", style:{"float":"left"}});
37211                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37212              }
37213             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37214             this.expandBtn.on("click", this.expand, this);
37215             
37216         }
37217         
37218         if(this.collapseBtn){
37219             this.collapseBtn.setVisible(c.collapsible == true);
37220         }
37221         
37222         this.cmargins = c.cmargins || this.cmargins ||
37223                          (this.position == "west" || this.position == "east" ?
37224                              {top: 0, left: 2, right:2, bottom: 0} :
37225                              {top: 2, left: 0, right:0, bottom: 2});
37226         */
37227         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37228         
37229         
37230         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37231         
37232         this.autoScroll = c.autoScroll || false;
37233         
37234         
37235        
37236         
37237         this.duration = c.duration || .30;
37238         this.slideDuration = c.slideDuration || .45;
37239         this.config = c;
37240        
37241     },
37242     /**
37243      * Returns true if this region is currently visible.
37244      * @return {Boolean}
37245      */
37246     isVisible : function(){
37247         return this.visible;
37248     },
37249
37250     /**
37251      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37252      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37253      */
37254     //setCollapsedTitle : function(title){
37255     //    title = title || "&#160;";
37256      //   if(this.collapsedTitleTextEl){
37257       //      this.collapsedTitleTextEl.innerHTML = title;
37258        // }
37259     //},
37260
37261     getBox : function(){
37262         var b;
37263       //  if(!this.collapsed){
37264             b = this.el.getBox(false, true);
37265        // }else{
37266           //  b = this.collapsedEl.getBox(false, true);
37267         //}
37268         return b;
37269     },
37270
37271     getMargins : function(){
37272         return this.margins;
37273         //return this.collapsed ? this.cmargins : this.margins;
37274     },
37275 /*
37276     highlight : function(){
37277         this.el.addClass("x-layout-panel-dragover");
37278     },
37279
37280     unhighlight : function(){
37281         this.el.removeClass("x-layout-panel-dragover");
37282     },
37283 */
37284     updateBox : function(box)
37285     {
37286         if (!this.bodyEl) {
37287             return; // not rendered yet..
37288         }
37289         
37290         this.box = box;
37291         if(!this.collapsed){
37292             this.el.dom.style.left = box.x + "px";
37293             this.el.dom.style.top = box.y + "px";
37294             this.updateBody(box.width, box.height);
37295         }else{
37296             this.collapsedEl.dom.style.left = box.x + "px";
37297             this.collapsedEl.dom.style.top = box.y + "px";
37298             this.collapsedEl.setSize(box.width, box.height);
37299         }
37300         if(this.tabs){
37301             this.tabs.autoSizeTabs();
37302         }
37303     },
37304
37305     updateBody : function(w, h)
37306     {
37307         if(w !== null){
37308             this.el.setWidth(w);
37309             w -= this.el.getBorderWidth("rl");
37310             if(this.config.adjustments){
37311                 w += this.config.adjustments[0];
37312             }
37313         }
37314         if(h !== null && h > 0){
37315             this.el.setHeight(h);
37316             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37317             h -= this.el.getBorderWidth("tb");
37318             if(this.config.adjustments){
37319                 h += this.config.adjustments[1];
37320             }
37321             this.bodyEl.setHeight(h);
37322             if(this.tabs){
37323                 h = this.tabs.syncHeight(h);
37324             }
37325         }
37326         if(this.panelSize){
37327             w = w !== null ? w : this.panelSize.width;
37328             h = h !== null ? h : this.panelSize.height;
37329         }
37330         if(this.activePanel){
37331             var el = this.activePanel.getEl();
37332             w = w !== null ? w : el.getWidth();
37333             h = h !== null ? h : el.getHeight();
37334             this.panelSize = {width: w, height: h};
37335             this.activePanel.setSize(w, h);
37336         }
37337         if(Roo.isIE && this.tabs){
37338             this.tabs.el.repaint();
37339         }
37340     },
37341
37342     /**
37343      * Returns the container element for this region.
37344      * @return {Roo.Element}
37345      */
37346     getEl : function(){
37347         return this.el;
37348     },
37349
37350     /**
37351      * Hides this region.
37352      */
37353     hide : function(){
37354         //if(!this.collapsed){
37355             this.el.dom.style.left = "-2000px";
37356             this.el.hide();
37357         //}else{
37358          //   this.collapsedEl.dom.style.left = "-2000px";
37359          //   this.collapsedEl.hide();
37360        // }
37361         this.visible = false;
37362         this.fireEvent("visibilitychange", this, false);
37363     },
37364
37365     /**
37366      * Shows this region if it was previously hidden.
37367      */
37368     show : function(){
37369         //if(!this.collapsed){
37370             this.el.show();
37371         //}else{
37372         //    this.collapsedEl.show();
37373        // }
37374         this.visible = true;
37375         this.fireEvent("visibilitychange", this, true);
37376     },
37377 /*
37378     closeClicked : function(){
37379         if(this.activePanel){
37380             this.remove(this.activePanel);
37381         }
37382     },
37383
37384     collapseClick : function(e){
37385         if(this.isSlid){
37386            e.stopPropagation();
37387            this.slideIn();
37388         }else{
37389            e.stopPropagation();
37390            this.slideOut();
37391         }
37392     },
37393 */
37394     /**
37395      * Collapses this region.
37396      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37397      */
37398     /*
37399     collapse : function(skipAnim, skipCheck = false){
37400         if(this.collapsed) {
37401             return;
37402         }
37403         
37404         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37405             
37406             this.collapsed = true;
37407             if(this.split){
37408                 this.split.el.hide();
37409             }
37410             if(this.config.animate && skipAnim !== true){
37411                 this.fireEvent("invalidated", this);
37412                 this.animateCollapse();
37413             }else{
37414                 this.el.setLocation(-20000,-20000);
37415                 this.el.hide();
37416                 this.collapsedEl.show();
37417                 this.fireEvent("collapsed", this);
37418                 this.fireEvent("invalidated", this);
37419             }
37420         }
37421         
37422     },
37423 */
37424     animateCollapse : function(){
37425         // overridden
37426     },
37427
37428     /**
37429      * Expands this region if it was previously collapsed.
37430      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37431      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37432      */
37433     /*
37434     expand : function(e, skipAnim){
37435         if(e) {
37436             e.stopPropagation();
37437         }
37438         if(!this.collapsed || this.el.hasActiveFx()) {
37439             return;
37440         }
37441         if(this.isSlid){
37442             this.afterSlideIn();
37443             skipAnim = true;
37444         }
37445         this.collapsed = false;
37446         if(this.config.animate && skipAnim !== true){
37447             this.animateExpand();
37448         }else{
37449             this.el.show();
37450             if(this.split){
37451                 this.split.el.show();
37452             }
37453             this.collapsedEl.setLocation(-2000,-2000);
37454             this.collapsedEl.hide();
37455             this.fireEvent("invalidated", this);
37456             this.fireEvent("expanded", this);
37457         }
37458     },
37459 */
37460     animateExpand : function(){
37461         // overridden
37462     },
37463
37464     initTabs : function()
37465     {
37466         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37467         
37468         var ts = new Roo.bootstrap.panel.Tabs({
37469             el: this.bodyEl.dom,
37470             region : this,
37471             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37472             disableTooltips: this.config.disableTabTips,
37473             toolbar : this.config.toolbar
37474         });
37475         
37476         if(this.config.hideTabs){
37477             ts.stripWrap.setDisplayed(false);
37478         }
37479         this.tabs = ts;
37480         ts.resizeTabs = this.config.resizeTabs === true;
37481         ts.minTabWidth = this.config.minTabWidth || 40;
37482         ts.maxTabWidth = this.config.maxTabWidth || 250;
37483         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37484         ts.monitorResize = false;
37485         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37486         ts.bodyEl.addClass('roo-layout-tabs-body');
37487         this.panels.each(this.initPanelAsTab, this);
37488     },
37489
37490     initPanelAsTab : function(panel){
37491         var ti = this.tabs.addTab(
37492             panel.getEl().id,
37493             panel.getTitle(),
37494             null,
37495             this.config.closeOnTab && panel.isClosable(),
37496             panel.tpl
37497         );
37498         if(panel.tabTip !== undefined){
37499             ti.setTooltip(panel.tabTip);
37500         }
37501         ti.on("activate", function(){
37502               this.setActivePanel(panel);
37503         }, this);
37504         
37505         if(this.config.closeOnTab){
37506             ti.on("beforeclose", function(t, e){
37507                 e.cancel = true;
37508                 this.remove(panel);
37509             }, this);
37510         }
37511         
37512         panel.tabItem = ti;
37513         
37514         return ti;
37515     },
37516
37517     updatePanelTitle : function(panel, title)
37518     {
37519         if(this.activePanel == panel){
37520             this.updateTitle(title);
37521         }
37522         if(this.tabs){
37523             var ti = this.tabs.getTab(panel.getEl().id);
37524             ti.setText(title);
37525             if(panel.tabTip !== undefined){
37526                 ti.setTooltip(panel.tabTip);
37527             }
37528         }
37529     },
37530
37531     updateTitle : function(title){
37532         if(this.titleTextEl && !this.config.title){
37533             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37534         }
37535     },
37536
37537     setActivePanel : function(panel)
37538     {
37539         panel = this.getPanel(panel);
37540         if(this.activePanel && this.activePanel != panel){
37541             if(this.activePanel.setActiveState(false) === false){
37542                 return;
37543             }
37544         }
37545         this.activePanel = panel;
37546         panel.setActiveState(true);
37547         if(this.panelSize){
37548             panel.setSize(this.panelSize.width, this.panelSize.height);
37549         }
37550         if(this.closeBtn){
37551             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37552         }
37553         this.updateTitle(panel.getTitle());
37554         if(this.tabs){
37555             this.fireEvent("invalidated", this);
37556         }
37557         this.fireEvent("panelactivated", this, panel);
37558     },
37559
37560     /**
37561      * Shows the specified panel.
37562      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37563      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37564      */
37565     showPanel : function(panel)
37566     {
37567         panel = this.getPanel(panel);
37568         if(panel){
37569             if(this.tabs){
37570                 var tab = this.tabs.getTab(panel.getEl().id);
37571                 if(tab.isHidden()){
37572                     this.tabs.unhideTab(tab.id);
37573                 }
37574                 tab.activate();
37575             }else{
37576                 this.setActivePanel(panel);
37577             }
37578         }
37579         return panel;
37580     },
37581
37582     /**
37583      * Get the active panel for this region.
37584      * @return {Roo.ContentPanel} The active panel or null
37585      */
37586     getActivePanel : function(){
37587         return this.activePanel;
37588     },
37589
37590     validateVisibility : function(){
37591         if(this.panels.getCount() < 1){
37592             this.updateTitle("&#160;");
37593             this.closeBtn.hide();
37594             this.hide();
37595         }else{
37596             if(!this.isVisible()){
37597                 this.show();
37598             }
37599         }
37600     },
37601
37602     /**
37603      * Adds the passed ContentPanel(s) to this region.
37604      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37605      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37606      */
37607     add : function(panel)
37608     {
37609         if(arguments.length > 1){
37610             for(var i = 0, len = arguments.length; i < len; i++) {
37611                 this.add(arguments[i]);
37612             }
37613             return null;
37614         }
37615         
37616         // if we have not been rendered yet, then we can not really do much of this..
37617         if (!this.bodyEl) {
37618             this.unrendered_panels.push(panel);
37619             return panel;
37620         }
37621         
37622         
37623         
37624         
37625         if(this.hasPanel(panel)){
37626             this.showPanel(panel);
37627             return panel;
37628         }
37629         panel.setRegion(this);
37630         this.panels.add(panel);
37631        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37632             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37633             // and hide them... ???
37634             this.bodyEl.dom.appendChild(panel.getEl().dom);
37635             if(panel.background !== true){
37636                 this.setActivePanel(panel);
37637             }
37638             this.fireEvent("paneladded", this, panel);
37639             return panel;
37640         }
37641         */
37642         if(!this.tabs){
37643             this.initTabs();
37644         }else{
37645             this.initPanelAsTab(panel);
37646         }
37647         
37648         
37649         if(panel.background !== true){
37650             this.tabs.activate(panel.getEl().id);
37651         }
37652         this.fireEvent("paneladded", this, panel);
37653         return panel;
37654     },
37655
37656     /**
37657      * Hides the tab for the specified panel.
37658      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37659      */
37660     hidePanel : function(panel){
37661         if(this.tabs && (panel = this.getPanel(panel))){
37662             this.tabs.hideTab(panel.getEl().id);
37663         }
37664     },
37665
37666     /**
37667      * Unhides the tab for a previously hidden panel.
37668      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37669      */
37670     unhidePanel : function(panel){
37671         if(this.tabs && (panel = this.getPanel(panel))){
37672             this.tabs.unhideTab(panel.getEl().id);
37673         }
37674     },
37675
37676     clearPanels : function(){
37677         while(this.panels.getCount() > 0){
37678              this.remove(this.panels.first());
37679         }
37680     },
37681
37682     /**
37683      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37684      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37685      * @param {Boolean} preservePanel Overrides the config preservePanel option
37686      * @return {Roo.ContentPanel} The panel that was removed
37687      */
37688     remove : function(panel, preservePanel)
37689     {
37690         panel = this.getPanel(panel);
37691         if(!panel){
37692             return null;
37693         }
37694         var e = {};
37695         this.fireEvent("beforeremove", this, panel, e);
37696         if(e.cancel === true){
37697             return null;
37698         }
37699         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37700         var panelId = panel.getId();
37701         this.panels.removeKey(panelId);
37702         if(preservePanel){
37703             document.body.appendChild(panel.getEl().dom);
37704         }
37705         if(this.tabs){
37706             this.tabs.removeTab(panel.getEl().id);
37707         }else if (!preservePanel){
37708             this.bodyEl.dom.removeChild(panel.getEl().dom);
37709         }
37710         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37711             var p = this.panels.first();
37712             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37713             tempEl.appendChild(p.getEl().dom);
37714             this.bodyEl.update("");
37715             this.bodyEl.dom.appendChild(p.getEl().dom);
37716             tempEl = null;
37717             this.updateTitle(p.getTitle());
37718             this.tabs = null;
37719             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37720             this.setActivePanel(p);
37721         }
37722         panel.setRegion(null);
37723         if(this.activePanel == panel){
37724             this.activePanel = null;
37725         }
37726         if(this.config.autoDestroy !== false && preservePanel !== true){
37727             try{panel.destroy();}catch(e){}
37728         }
37729         this.fireEvent("panelremoved", this, panel);
37730         return panel;
37731     },
37732
37733     /**
37734      * Returns the TabPanel component used by this region
37735      * @return {Roo.TabPanel}
37736      */
37737     getTabs : function(){
37738         return this.tabs;
37739     },
37740
37741     createTool : function(parentEl, className){
37742         var btn = Roo.DomHelper.append(parentEl, {
37743             tag: "div",
37744             cls: "x-layout-tools-button",
37745             children: [ {
37746                 tag: "div",
37747                 cls: "roo-layout-tools-button-inner " + className,
37748                 html: "&#160;"
37749             }]
37750         }, true);
37751         btn.addClassOnOver("roo-layout-tools-button-over");
37752         return btn;
37753     }
37754 });/*
37755  * Based on:
37756  * Ext JS Library 1.1.1
37757  * Copyright(c) 2006-2007, Ext JS, LLC.
37758  *
37759  * Originally Released Under LGPL - original licence link has changed is not relivant.
37760  *
37761  * Fork - LGPL
37762  * <script type="text/javascript">
37763  */
37764  
37765
37766
37767 /**
37768  * @class Roo.SplitLayoutRegion
37769  * @extends Roo.LayoutRegion
37770  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37771  */
37772 Roo.bootstrap.layout.Split = function(config){
37773     this.cursor = config.cursor;
37774     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37775 };
37776
37777 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37778 {
37779     splitTip : "Drag to resize.",
37780     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37781     useSplitTips : false,
37782
37783     applyConfig : function(config){
37784         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37785     },
37786     
37787     onRender : function(ctr,pos) {
37788         
37789         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37790         if(!this.config.split){
37791             return;
37792         }
37793         if(!this.split){
37794             
37795             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37796                             tag: "div",
37797                             id: this.el.id + "-split",
37798                             cls: "roo-layout-split roo-layout-split-"+this.position,
37799                             html: "&#160;"
37800             });
37801             /** The SplitBar for this region 
37802             * @type Roo.SplitBar */
37803             // does not exist yet...
37804             Roo.log([this.position, this.orientation]);
37805             
37806             this.split = new Roo.bootstrap.SplitBar({
37807                 dragElement : splitEl,
37808                 resizingElement: this.el,
37809                 orientation : this.orientation
37810             });
37811             
37812             this.split.on("moved", this.onSplitMove, this);
37813             this.split.useShim = this.config.useShim === true;
37814             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37815             if(this.useSplitTips){
37816                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37817             }
37818             //if(config.collapsible){
37819             //    this.split.el.on("dblclick", this.collapse,  this);
37820             //}
37821         }
37822         if(typeof this.config.minSize != "undefined"){
37823             this.split.minSize = this.config.minSize;
37824         }
37825         if(typeof this.config.maxSize != "undefined"){
37826             this.split.maxSize = this.config.maxSize;
37827         }
37828         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37829             this.hideSplitter();
37830         }
37831         
37832     },
37833
37834     getHMaxSize : function(){
37835          var cmax = this.config.maxSize || 10000;
37836          var center = this.mgr.getRegion("center");
37837          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37838     },
37839
37840     getVMaxSize : function(){
37841          var cmax = this.config.maxSize || 10000;
37842          var center = this.mgr.getRegion("center");
37843          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37844     },
37845
37846     onSplitMove : function(split, newSize){
37847         this.fireEvent("resized", this, newSize);
37848     },
37849     
37850     /** 
37851      * Returns the {@link Roo.SplitBar} for this region.
37852      * @return {Roo.SplitBar}
37853      */
37854     getSplitBar : function(){
37855         return this.split;
37856     },
37857     
37858     hide : function(){
37859         this.hideSplitter();
37860         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37861     },
37862
37863     hideSplitter : function(){
37864         if(this.split){
37865             this.split.el.setLocation(-2000,-2000);
37866             this.split.el.hide();
37867         }
37868     },
37869
37870     show : function(){
37871         if(this.split){
37872             this.split.el.show();
37873         }
37874         Roo.bootstrap.layout.Split.superclass.show.call(this);
37875     },
37876     
37877     beforeSlide: function(){
37878         if(Roo.isGecko){// firefox overflow auto bug workaround
37879             this.bodyEl.clip();
37880             if(this.tabs) {
37881                 this.tabs.bodyEl.clip();
37882             }
37883             if(this.activePanel){
37884                 this.activePanel.getEl().clip();
37885                 
37886                 if(this.activePanel.beforeSlide){
37887                     this.activePanel.beforeSlide();
37888                 }
37889             }
37890         }
37891     },
37892     
37893     afterSlide : function(){
37894         if(Roo.isGecko){// firefox overflow auto bug workaround
37895             this.bodyEl.unclip();
37896             if(this.tabs) {
37897                 this.tabs.bodyEl.unclip();
37898             }
37899             if(this.activePanel){
37900                 this.activePanel.getEl().unclip();
37901                 if(this.activePanel.afterSlide){
37902                     this.activePanel.afterSlide();
37903                 }
37904             }
37905         }
37906     },
37907
37908     initAutoHide : function(){
37909         if(this.autoHide !== false){
37910             if(!this.autoHideHd){
37911                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37912                 this.autoHideHd = {
37913                     "mouseout": function(e){
37914                         if(!e.within(this.el, true)){
37915                             st.delay(500);
37916                         }
37917                     },
37918                     "mouseover" : function(e){
37919                         st.cancel();
37920                     },
37921                     scope : this
37922                 };
37923             }
37924             this.el.on(this.autoHideHd);
37925         }
37926     },
37927
37928     clearAutoHide : function(){
37929         if(this.autoHide !== false){
37930             this.el.un("mouseout", this.autoHideHd.mouseout);
37931             this.el.un("mouseover", this.autoHideHd.mouseover);
37932         }
37933     },
37934
37935     clearMonitor : function(){
37936         Roo.get(document).un("click", this.slideInIf, this);
37937     },
37938
37939     // these names are backwards but not changed for compat
37940     slideOut : function(){
37941         if(this.isSlid || this.el.hasActiveFx()){
37942             return;
37943         }
37944         this.isSlid = true;
37945         if(this.collapseBtn){
37946             this.collapseBtn.hide();
37947         }
37948         this.closeBtnState = this.closeBtn.getStyle('display');
37949         this.closeBtn.hide();
37950         if(this.stickBtn){
37951             this.stickBtn.show();
37952         }
37953         this.el.show();
37954         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37955         this.beforeSlide();
37956         this.el.setStyle("z-index", 10001);
37957         this.el.slideIn(this.getSlideAnchor(), {
37958             callback: function(){
37959                 this.afterSlide();
37960                 this.initAutoHide();
37961                 Roo.get(document).on("click", this.slideInIf, this);
37962                 this.fireEvent("slideshow", this);
37963             },
37964             scope: this,
37965             block: true
37966         });
37967     },
37968
37969     afterSlideIn : function(){
37970         this.clearAutoHide();
37971         this.isSlid = false;
37972         this.clearMonitor();
37973         this.el.setStyle("z-index", "");
37974         if(this.collapseBtn){
37975             this.collapseBtn.show();
37976         }
37977         this.closeBtn.setStyle('display', this.closeBtnState);
37978         if(this.stickBtn){
37979             this.stickBtn.hide();
37980         }
37981         this.fireEvent("slidehide", this);
37982     },
37983
37984     slideIn : function(cb){
37985         if(!this.isSlid || this.el.hasActiveFx()){
37986             Roo.callback(cb);
37987             return;
37988         }
37989         this.isSlid = false;
37990         this.beforeSlide();
37991         this.el.slideOut(this.getSlideAnchor(), {
37992             callback: function(){
37993                 this.el.setLeftTop(-10000, -10000);
37994                 this.afterSlide();
37995                 this.afterSlideIn();
37996                 Roo.callback(cb);
37997             },
37998             scope: this,
37999             block: true
38000         });
38001     },
38002     
38003     slideInIf : function(e){
38004         if(!e.within(this.el)){
38005             this.slideIn();
38006         }
38007     },
38008
38009     animateCollapse : function(){
38010         this.beforeSlide();
38011         this.el.setStyle("z-index", 20000);
38012         var anchor = this.getSlideAnchor();
38013         this.el.slideOut(anchor, {
38014             callback : function(){
38015                 this.el.setStyle("z-index", "");
38016                 this.collapsedEl.slideIn(anchor, {duration:.3});
38017                 this.afterSlide();
38018                 this.el.setLocation(-10000,-10000);
38019                 this.el.hide();
38020                 this.fireEvent("collapsed", this);
38021             },
38022             scope: this,
38023             block: true
38024         });
38025     },
38026
38027     animateExpand : function(){
38028         this.beforeSlide();
38029         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38030         this.el.setStyle("z-index", 20000);
38031         this.collapsedEl.hide({
38032             duration:.1
38033         });
38034         this.el.slideIn(this.getSlideAnchor(), {
38035             callback : function(){
38036                 this.el.setStyle("z-index", "");
38037                 this.afterSlide();
38038                 if(this.split){
38039                     this.split.el.show();
38040                 }
38041                 this.fireEvent("invalidated", this);
38042                 this.fireEvent("expanded", this);
38043             },
38044             scope: this,
38045             block: true
38046         });
38047     },
38048
38049     anchors : {
38050         "west" : "left",
38051         "east" : "right",
38052         "north" : "top",
38053         "south" : "bottom"
38054     },
38055
38056     sanchors : {
38057         "west" : "l",
38058         "east" : "r",
38059         "north" : "t",
38060         "south" : "b"
38061     },
38062
38063     canchors : {
38064         "west" : "tl-tr",
38065         "east" : "tr-tl",
38066         "north" : "tl-bl",
38067         "south" : "bl-tl"
38068     },
38069
38070     getAnchor : function(){
38071         return this.anchors[this.position];
38072     },
38073
38074     getCollapseAnchor : function(){
38075         return this.canchors[this.position];
38076     },
38077
38078     getSlideAnchor : function(){
38079         return this.sanchors[this.position];
38080     },
38081
38082     getAlignAdj : function(){
38083         var cm = this.cmargins;
38084         switch(this.position){
38085             case "west":
38086                 return [0, 0];
38087             break;
38088             case "east":
38089                 return [0, 0];
38090             break;
38091             case "north":
38092                 return [0, 0];
38093             break;
38094             case "south":
38095                 return [0, 0];
38096             break;
38097         }
38098     },
38099
38100     getExpandAdj : function(){
38101         var c = this.collapsedEl, cm = this.cmargins;
38102         switch(this.position){
38103             case "west":
38104                 return [-(cm.right+c.getWidth()+cm.left), 0];
38105             break;
38106             case "east":
38107                 return [cm.right+c.getWidth()+cm.left, 0];
38108             break;
38109             case "north":
38110                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38111             break;
38112             case "south":
38113                 return [0, cm.top+cm.bottom+c.getHeight()];
38114             break;
38115         }
38116     }
38117 });/*
38118  * Based on:
38119  * Ext JS Library 1.1.1
38120  * Copyright(c) 2006-2007, Ext JS, LLC.
38121  *
38122  * Originally Released Under LGPL - original licence link has changed is not relivant.
38123  *
38124  * Fork - LGPL
38125  * <script type="text/javascript">
38126  */
38127 /*
38128  * These classes are private internal classes
38129  */
38130 Roo.bootstrap.layout.Center = function(config){
38131     config.region = "center";
38132     Roo.bootstrap.layout.Region.call(this, config);
38133     this.visible = true;
38134     this.minWidth = config.minWidth || 20;
38135     this.minHeight = config.minHeight || 20;
38136 };
38137
38138 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38139     hide : function(){
38140         // center panel can't be hidden
38141     },
38142     
38143     show : function(){
38144         // center panel can't be hidden
38145     },
38146     
38147     getMinWidth: function(){
38148         return this.minWidth;
38149     },
38150     
38151     getMinHeight: function(){
38152         return this.minHeight;
38153     }
38154 });
38155
38156
38157
38158
38159  
38160
38161
38162
38163
38164
38165
38166 Roo.bootstrap.layout.North = function(config)
38167 {
38168     config.region = 'north';
38169     config.cursor = 'n-resize';
38170     
38171     Roo.bootstrap.layout.Split.call(this, config);
38172     
38173     
38174     if(this.split){
38175         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38176         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38177         this.split.el.addClass("roo-layout-split-v");
38178     }
38179     var size = config.initialSize || config.height;
38180     if(typeof size != "undefined"){
38181         this.el.setHeight(size);
38182     }
38183 };
38184 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38185 {
38186     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38187     
38188     
38189     
38190     getBox : function(){
38191         if(this.collapsed){
38192             return this.collapsedEl.getBox();
38193         }
38194         var box = this.el.getBox();
38195         if(this.split){
38196             box.height += this.split.el.getHeight();
38197         }
38198         return box;
38199     },
38200     
38201     updateBox : function(box){
38202         if(this.split && !this.collapsed){
38203             box.height -= this.split.el.getHeight();
38204             this.split.el.setLeft(box.x);
38205             this.split.el.setTop(box.y+box.height);
38206             this.split.el.setWidth(box.width);
38207         }
38208         if(this.collapsed){
38209             this.updateBody(box.width, null);
38210         }
38211         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38212     }
38213 });
38214
38215
38216
38217
38218
38219 Roo.bootstrap.layout.South = function(config){
38220     config.region = 'south';
38221     config.cursor = 's-resize';
38222     Roo.bootstrap.layout.Split.call(this, config);
38223     if(this.split){
38224         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38225         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38226         this.split.el.addClass("roo-layout-split-v");
38227     }
38228     var size = config.initialSize || config.height;
38229     if(typeof size != "undefined"){
38230         this.el.setHeight(size);
38231     }
38232 };
38233
38234 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38235     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38236     getBox : function(){
38237         if(this.collapsed){
38238             return this.collapsedEl.getBox();
38239         }
38240         var box = this.el.getBox();
38241         if(this.split){
38242             var sh = this.split.el.getHeight();
38243             box.height += sh;
38244             box.y -= sh;
38245         }
38246         return box;
38247     },
38248     
38249     updateBox : function(box){
38250         if(this.split && !this.collapsed){
38251             var sh = this.split.el.getHeight();
38252             box.height -= sh;
38253             box.y += sh;
38254             this.split.el.setLeft(box.x);
38255             this.split.el.setTop(box.y-sh);
38256             this.split.el.setWidth(box.width);
38257         }
38258         if(this.collapsed){
38259             this.updateBody(box.width, null);
38260         }
38261         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38262     }
38263 });
38264
38265 Roo.bootstrap.layout.East = function(config){
38266     config.region = "east";
38267     config.cursor = "e-resize";
38268     Roo.bootstrap.layout.Split.call(this, config);
38269     if(this.split){
38270         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38271         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38272         this.split.el.addClass("roo-layout-split-h");
38273     }
38274     var size = config.initialSize || config.width;
38275     if(typeof size != "undefined"){
38276         this.el.setWidth(size);
38277     }
38278 };
38279 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38280     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38281     getBox : function(){
38282         if(this.collapsed){
38283             return this.collapsedEl.getBox();
38284         }
38285         var box = this.el.getBox();
38286         if(this.split){
38287             var sw = this.split.el.getWidth();
38288             box.width += sw;
38289             box.x -= sw;
38290         }
38291         return box;
38292     },
38293
38294     updateBox : function(box){
38295         if(this.split && !this.collapsed){
38296             var sw = this.split.el.getWidth();
38297             box.width -= sw;
38298             this.split.el.setLeft(box.x);
38299             this.split.el.setTop(box.y);
38300             this.split.el.setHeight(box.height);
38301             box.x += sw;
38302         }
38303         if(this.collapsed){
38304             this.updateBody(null, box.height);
38305         }
38306         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38307     }
38308 });
38309
38310 Roo.bootstrap.layout.West = function(config){
38311     config.region = "west";
38312     config.cursor = "w-resize";
38313     
38314     Roo.bootstrap.layout.Split.call(this, config);
38315     if(this.split){
38316         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38317         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38318         this.split.el.addClass("roo-layout-split-h");
38319     }
38320     
38321 };
38322 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38323     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38324     
38325     onRender: function(ctr, pos)
38326     {
38327         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38328         var size = this.config.initialSize || this.config.width;
38329         if(typeof size != "undefined"){
38330             this.el.setWidth(size);
38331         }
38332     },
38333     
38334     getBox : function(){
38335         if(this.collapsed){
38336             return this.collapsedEl.getBox();
38337         }
38338         var box = this.el.getBox();
38339         if(this.split){
38340             box.width += this.split.el.getWidth();
38341         }
38342         return box;
38343     },
38344     
38345     updateBox : function(box){
38346         if(this.split && !this.collapsed){
38347             var sw = this.split.el.getWidth();
38348             box.width -= sw;
38349             this.split.el.setLeft(box.x+box.width);
38350             this.split.el.setTop(box.y);
38351             this.split.el.setHeight(box.height);
38352         }
38353         if(this.collapsed){
38354             this.updateBody(null, box.height);
38355         }
38356         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38357     }
38358 });Roo.namespace("Roo.bootstrap.panel");/*
38359  * Based on:
38360  * Ext JS Library 1.1.1
38361  * Copyright(c) 2006-2007, Ext JS, LLC.
38362  *
38363  * Originally Released Under LGPL - original licence link has changed is not relivant.
38364  *
38365  * Fork - LGPL
38366  * <script type="text/javascript">
38367  */
38368 /**
38369  * @class Roo.ContentPanel
38370  * @extends Roo.util.Observable
38371  * A basic ContentPanel element.
38372  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38373  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38374  * @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
38375  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38376  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38377  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38378  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38379  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38380  * @cfg {String} title          The title for this panel
38381  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38382  * @cfg {String} url            Calls {@link #setUrl} with this value
38383  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38384  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38385  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38386  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38387  * @cfg {Boolean} badges render the badges
38388
38389  * @constructor
38390  * Create a new ContentPanel.
38391  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38392  * @param {String/Object} config A string to set only the title or a config object
38393  * @param {String} content (optional) Set the HTML content for this panel
38394  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38395  */
38396 Roo.bootstrap.panel.Content = function( config){
38397     
38398     this.tpl = config.tpl || false;
38399     
38400     var el = config.el;
38401     var content = config.content;
38402
38403     if(config.autoCreate){ // xtype is available if this is called from factory
38404         el = Roo.id();
38405     }
38406     this.el = Roo.get(el);
38407     if(!this.el && config && config.autoCreate){
38408         if(typeof config.autoCreate == "object"){
38409             if(!config.autoCreate.id){
38410                 config.autoCreate.id = config.id||el;
38411             }
38412             this.el = Roo.DomHelper.append(document.body,
38413                         config.autoCreate, true);
38414         }else{
38415             var elcfg =  {   tag: "div",
38416                             cls: "roo-layout-inactive-content",
38417                             id: config.id||el
38418                             };
38419             if (config.html) {
38420                 elcfg.html = config.html;
38421                 
38422             }
38423                         
38424             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38425         }
38426     } 
38427     this.closable = false;
38428     this.loaded = false;
38429     this.active = false;
38430    
38431       
38432     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38433         
38434         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38435         
38436         this.wrapEl = this.el; //this.el.wrap();
38437         var ti = [];
38438         if (config.toolbar.items) {
38439             ti = config.toolbar.items ;
38440             delete config.toolbar.items ;
38441         }
38442         
38443         var nitems = [];
38444         this.toolbar.render(this.wrapEl, 'before');
38445         for(var i =0;i < ti.length;i++) {
38446           //  Roo.log(['add child', items[i]]);
38447             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38448         }
38449         this.toolbar.items = nitems;
38450         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38451         delete config.toolbar;
38452         
38453     }
38454     /*
38455     // xtype created footer. - not sure if will work as we normally have to render first..
38456     if (this.footer && !this.footer.el && this.footer.xtype) {
38457         if (!this.wrapEl) {
38458             this.wrapEl = this.el.wrap();
38459         }
38460     
38461         this.footer.container = this.wrapEl.createChild();
38462          
38463         this.footer = Roo.factory(this.footer, Roo);
38464         
38465     }
38466     */
38467     
38468      if(typeof config == "string"){
38469         this.title = config;
38470     }else{
38471         Roo.apply(this, config);
38472     }
38473     
38474     if(this.resizeEl){
38475         this.resizeEl = Roo.get(this.resizeEl, true);
38476     }else{
38477         this.resizeEl = this.el;
38478     }
38479     // handle view.xtype
38480     
38481  
38482     
38483     
38484     this.addEvents({
38485         /**
38486          * @event activate
38487          * Fires when this panel is activated. 
38488          * @param {Roo.ContentPanel} this
38489          */
38490         "activate" : true,
38491         /**
38492          * @event deactivate
38493          * Fires when this panel is activated. 
38494          * @param {Roo.ContentPanel} this
38495          */
38496         "deactivate" : true,
38497
38498         /**
38499          * @event resize
38500          * Fires when this panel is resized if fitToFrame is true.
38501          * @param {Roo.ContentPanel} this
38502          * @param {Number} width The width after any component adjustments
38503          * @param {Number} height The height after any component adjustments
38504          */
38505         "resize" : true,
38506         
38507          /**
38508          * @event render
38509          * Fires when this tab is created
38510          * @param {Roo.ContentPanel} this
38511          */
38512         "render" : true
38513         
38514         
38515         
38516     });
38517     
38518
38519     
38520     
38521     if(this.autoScroll){
38522         this.resizeEl.setStyle("overflow", "auto");
38523     } else {
38524         // fix randome scrolling
38525         //this.el.on('scroll', function() {
38526         //    Roo.log('fix random scolling');
38527         //    this.scrollTo('top',0); 
38528         //});
38529     }
38530     content = content || this.content;
38531     if(content){
38532         this.setContent(content);
38533     }
38534     if(config && config.url){
38535         this.setUrl(this.url, this.params, this.loadOnce);
38536     }
38537     
38538     
38539     
38540     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38541     
38542     if (this.view && typeof(this.view.xtype) != 'undefined') {
38543         this.view.el = this.el.appendChild(document.createElement("div"));
38544         this.view = Roo.factory(this.view); 
38545         this.view.render  &&  this.view.render(false, '');  
38546     }
38547     
38548     
38549     this.fireEvent('render', this);
38550 };
38551
38552 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38553     
38554     tabTip : '',
38555     
38556     setRegion : function(region){
38557         this.region = region;
38558         this.setActiveClass(region && !this.background);
38559     },
38560     
38561     
38562     setActiveClass: function(state)
38563     {
38564         if(state){
38565            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38566            this.el.setStyle('position','relative');
38567         }else{
38568            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38569            this.el.setStyle('position', 'absolute');
38570         } 
38571     },
38572     
38573     /**
38574      * Returns the toolbar for this Panel if one was configured. 
38575      * @return {Roo.Toolbar} 
38576      */
38577     getToolbar : function(){
38578         return this.toolbar;
38579     },
38580     
38581     setActiveState : function(active)
38582     {
38583         this.active = active;
38584         this.setActiveClass(active);
38585         if(!active){
38586             if(this.fireEvent("deactivate", this) === false){
38587                 return false;
38588             }
38589             return true;
38590         }
38591         this.fireEvent("activate", this);
38592         return true;
38593     },
38594     /**
38595      * Updates this panel's element
38596      * @param {String} content The new content
38597      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38598     */
38599     setContent : function(content, loadScripts){
38600         this.el.update(content, loadScripts);
38601     },
38602
38603     ignoreResize : function(w, h){
38604         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38605             return true;
38606         }else{
38607             this.lastSize = {width: w, height: h};
38608             return false;
38609         }
38610     },
38611     /**
38612      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38613      * @return {Roo.UpdateManager} The UpdateManager
38614      */
38615     getUpdateManager : function(){
38616         return this.el.getUpdateManager();
38617     },
38618      /**
38619      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38620      * @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:
38621 <pre><code>
38622 panel.load({
38623     url: "your-url.php",
38624     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38625     callback: yourFunction,
38626     scope: yourObject, //(optional scope)
38627     discardUrl: false,
38628     nocache: false,
38629     text: "Loading...",
38630     timeout: 30,
38631     scripts: false
38632 });
38633 </code></pre>
38634      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38635      * 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.
38636      * @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}
38637      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38638      * @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.
38639      * @return {Roo.ContentPanel} this
38640      */
38641     load : function(){
38642         var um = this.el.getUpdateManager();
38643         um.update.apply(um, arguments);
38644         return this;
38645     },
38646
38647
38648     /**
38649      * 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.
38650      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38651      * @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)
38652      * @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)
38653      * @return {Roo.UpdateManager} The UpdateManager
38654      */
38655     setUrl : function(url, params, loadOnce){
38656         if(this.refreshDelegate){
38657             this.removeListener("activate", this.refreshDelegate);
38658         }
38659         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38660         this.on("activate", this.refreshDelegate);
38661         return this.el.getUpdateManager();
38662     },
38663     
38664     _handleRefresh : function(url, params, loadOnce){
38665         if(!loadOnce || !this.loaded){
38666             var updater = this.el.getUpdateManager();
38667             updater.update(url, params, this._setLoaded.createDelegate(this));
38668         }
38669     },
38670     
38671     _setLoaded : function(){
38672         this.loaded = true;
38673     }, 
38674     
38675     /**
38676      * Returns this panel's id
38677      * @return {String} 
38678      */
38679     getId : function(){
38680         return this.el.id;
38681     },
38682     
38683     /** 
38684      * Returns this panel's element - used by regiosn to add.
38685      * @return {Roo.Element} 
38686      */
38687     getEl : function(){
38688         return this.wrapEl || this.el;
38689     },
38690     
38691    
38692     
38693     adjustForComponents : function(width, height)
38694     {
38695         //Roo.log('adjustForComponents ');
38696         if(this.resizeEl != this.el){
38697             width -= this.el.getFrameWidth('lr');
38698             height -= this.el.getFrameWidth('tb');
38699         }
38700         if(this.toolbar){
38701             var te = this.toolbar.getEl();
38702             te.setWidth(width);
38703             height -= te.getHeight();
38704         }
38705         if(this.footer){
38706             var te = this.footer.getEl();
38707             te.setWidth(width);
38708             height -= te.getHeight();
38709         }
38710         
38711         
38712         if(this.adjustments){
38713             width += this.adjustments[0];
38714             height += this.adjustments[1];
38715         }
38716         return {"width": width, "height": height};
38717     },
38718     
38719     setSize : function(width, height){
38720         if(this.fitToFrame && !this.ignoreResize(width, height)){
38721             if(this.fitContainer && this.resizeEl != this.el){
38722                 this.el.setSize(width, height);
38723             }
38724             var size = this.adjustForComponents(width, height);
38725             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38726             this.fireEvent('resize', this, size.width, size.height);
38727         }
38728     },
38729     
38730     /**
38731      * Returns this panel's title
38732      * @return {String} 
38733      */
38734     getTitle : function(){
38735         
38736         if (typeof(this.title) != 'object') {
38737             return this.title;
38738         }
38739         
38740         var t = '';
38741         for (var k in this.title) {
38742             if (!this.title.hasOwnProperty(k)) {
38743                 continue;
38744             }
38745             
38746             if (k.indexOf('-') >= 0) {
38747                 var s = k.split('-');
38748                 for (var i = 0; i<s.length; i++) {
38749                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38750                 }
38751             } else {
38752                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38753             }
38754         }
38755         return t;
38756     },
38757     
38758     /**
38759      * Set this panel's title
38760      * @param {String} title
38761      */
38762     setTitle : function(title){
38763         this.title = title;
38764         if(this.region){
38765             this.region.updatePanelTitle(this, title);
38766         }
38767     },
38768     
38769     /**
38770      * Returns true is this panel was configured to be closable
38771      * @return {Boolean} 
38772      */
38773     isClosable : function(){
38774         return this.closable;
38775     },
38776     
38777     beforeSlide : function(){
38778         this.el.clip();
38779         this.resizeEl.clip();
38780     },
38781     
38782     afterSlide : function(){
38783         this.el.unclip();
38784         this.resizeEl.unclip();
38785     },
38786     
38787     /**
38788      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38789      *   Will fail silently if the {@link #setUrl} method has not been called.
38790      *   This does not activate the panel, just updates its content.
38791      */
38792     refresh : function(){
38793         if(this.refreshDelegate){
38794            this.loaded = false;
38795            this.refreshDelegate();
38796         }
38797     },
38798     
38799     /**
38800      * Destroys this panel
38801      */
38802     destroy : function(){
38803         this.el.removeAllListeners();
38804         var tempEl = document.createElement("span");
38805         tempEl.appendChild(this.el.dom);
38806         tempEl.innerHTML = "";
38807         this.el.remove();
38808         this.el = null;
38809     },
38810     
38811     /**
38812      * form - if the content panel contains a form - this is a reference to it.
38813      * @type {Roo.form.Form}
38814      */
38815     form : false,
38816     /**
38817      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38818      *    This contains a reference to it.
38819      * @type {Roo.View}
38820      */
38821     view : false,
38822     
38823       /**
38824      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38825      * <pre><code>
38826
38827 layout.addxtype({
38828        xtype : 'Form',
38829        items: [ .... ]
38830    }
38831 );
38832
38833 </code></pre>
38834      * @param {Object} cfg Xtype definition of item to add.
38835      */
38836     
38837     
38838     getChildContainer: function () {
38839         return this.getEl();
38840     }
38841     
38842     
38843     /*
38844         var  ret = new Roo.factory(cfg);
38845         return ret;
38846         
38847         
38848         // add form..
38849         if (cfg.xtype.match(/^Form$/)) {
38850             
38851             var el;
38852             //if (this.footer) {
38853             //    el = this.footer.container.insertSibling(false, 'before');
38854             //} else {
38855                 el = this.el.createChild();
38856             //}
38857
38858             this.form = new  Roo.form.Form(cfg);
38859             
38860             
38861             if ( this.form.allItems.length) {
38862                 this.form.render(el.dom);
38863             }
38864             return this.form;
38865         }
38866         // should only have one of theses..
38867         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38868             // views.. should not be just added - used named prop 'view''
38869             
38870             cfg.el = this.el.appendChild(document.createElement("div"));
38871             // factory?
38872             
38873             var ret = new Roo.factory(cfg);
38874              
38875              ret.render && ret.render(false, ''); // render blank..
38876             this.view = ret;
38877             return ret;
38878         }
38879         return false;
38880     }
38881     \*/
38882 });
38883  
38884 /**
38885  * @class Roo.bootstrap.panel.Grid
38886  * @extends Roo.bootstrap.panel.Content
38887  * @constructor
38888  * Create a new GridPanel.
38889  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38890  * @param {Object} config A the config object
38891   
38892  */
38893
38894
38895
38896 Roo.bootstrap.panel.Grid = function(config)
38897 {
38898     
38899       
38900     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38901         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38902
38903     config.el = this.wrapper;
38904     //this.el = this.wrapper;
38905     
38906       if (config.container) {
38907         // ctor'ed from a Border/panel.grid
38908         
38909         
38910         this.wrapper.setStyle("overflow", "hidden");
38911         this.wrapper.addClass('roo-grid-container');
38912
38913     }
38914     
38915     
38916     if(config.toolbar){
38917         var tool_el = this.wrapper.createChild();    
38918         this.toolbar = Roo.factory(config.toolbar);
38919         var ti = [];
38920         if (config.toolbar.items) {
38921             ti = config.toolbar.items ;
38922             delete config.toolbar.items ;
38923         }
38924         
38925         var nitems = [];
38926         this.toolbar.render(tool_el);
38927         for(var i =0;i < ti.length;i++) {
38928           //  Roo.log(['add child', items[i]]);
38929             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38930         }
38931         this.toolbar.items = nitems;
38932         
38933         delete config.toolbar;
38934     }
38935     
38936     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38937     config.grid.scrollBody = true;;
38938     config.grid.monitorWindowResize = false; // turn off autosizing
38939     config.grid.autoHeight = false;
38940     config.grid.autoWidth = false;
38941     
38942     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38943     
38944     if (config.background) {
38945         // render grid on panel activation (if panel background)
38946         this.on('activate', function(gp) {
38947             if (!gp.grid.rendered) {
38948                 gp.grid.render(this.wrapper);
38949                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38950             }
38951         });
38952             
38953     } else {
38954         this.grid.render(this.wrapper);
38955         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38956
38957     }
38958     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38959     // ??? needed ??? config.el = this.wrapper;
38960     
38961     
38962     
38963   
38964     // xtype created footer. - not sure if will work as we normally have to render first..
38965     if (this.footer && !this.footer.el && this.footer.xtype) {
38966         
38967         var ctr = this.grid.getView().getFooterPanel(true);
38968         this.footer.dataSource = this.grid.dataSource;
38969         this.footer = Roo.factory(this.footer, Roo);
38970         this.footer.render(ctr);
38971         
38972     }
38973     
38974     
38975     
38976     
38977      
38978 };
38979
38980 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38981     getId : function(){
38982         return this.grid.id;
38983     },
38984     
38985     /**
38986      * Returns the grid for this panel
38987      * @return {Roo.bootstrap.Table} 
38988      */
38989     getGrid : function(){
38990         return this.grid;    
38991     },
38992     
38993     setSize : function(width, height){
38994         if(!this.ignoreResize(width, height)){
38995             var grid = this.grid;
38996             var size = this.adjustForComponents(width, height);
38997             var gridel = grid.getGridEl();
38998             gridel.setSize(size.width, size.height);
38999             /*
39000             var thd = grid.getGridEl().select('thead',true).first();
39001             var tbd = grid.getGridEl().select('tbody', true).first();
39002             if (tbd) {
39003                 tbd.setSize(width, height - thd.getHeight());
39004             }
39005             */
39006             grid.autoSize();
39007         }
39008     },
39009      
39010     
39011     
39012     beforeSlide : function(){
39013         this.grid.getView().scroller.clip();
39014     },
39015     
39016     afterSlide : function(){
39017         this.grid.getView().scroller.unclip();
39018     },
39019     
39020     destroy : function(){
39021         this.grid.destroy();
39022         delete this.grid;
39023         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39024     }
39025 });
39026
39027 /**
39028  * @class Roo.bootstrap.panel.Nest
39029  * @extends Roo.bootstrap.panel.Content
39030  * @constructor
39031  * Create a new Panel, that can contain a layout.Border.
39032  * 
39033  * 
39034  * @param {Roo.BorderLayout} layout The layout for this panel
39035  * @param {String/Object} config A string to set only the title or a config object
39036  */
39037 Roo.bootstrap.panel.Nest = function(config)
39038 {
39039     // construct with only one argument..
39040     /* FIXME - implement nicer consturctors
39041     if (layout.layout) {
39042         config = layout;
39043         layout = config.layout;
39044         delete config.layout;
39045     }
39046     if (layout.xtype && !layout.getEl) {
39047         // then layout needs constructing..
39048         layout = Roo.factory(layout, Roo);
39049     }
39050     */
39051     
39052     config.el =  config.layout.getEl();
39053     
39054     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39055     
39056     config.layout.monitorWindowResize = false; // turn off autosizing
39057     this.layout = config.layout;
39058     this.layout.getEl().addClass("roo-layout-nested-layout");
39059     this.layout.parent = this;
39060     
39061     
39062     
39063     
39064 };
39065
39066 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39067
39068     setSize : function(width, height){
39069         if(!this.ignoreResize(width, height)){
39070             var size = this.adjustForComponents(width, height);
39071             var el = this.layout.getEl();
39072             if (size.height < 1) {
39073                 el.setWidth(size.width);   
39074             } else {
39075                 el.setSize(size.width, size.height);
39076             }
39077             var touch = el.dom.offsetWidth;
39078             this.layout.layout();
39079             // ie requires a double layout on the first pass
39080             if(Roo.isIE && !this.initialized){
39081                 this.initialized = true;
39082                 this.layout.layout();
39083             }
39084         }
39085     },
39086     
39087     // activate all subpanels if not currently active..
39088     
39089     setActiveState : function(active){
39090         this.active = active;
39091         this.setActiveClass(active);
39092         
39093         if(!active){
39094             this.fireEvent("deactivate", this);
39095             return;
39096         }
39097         
39098         this.fireEvent("activate", this);
39099         // not sure if this should happen before or after..
39100         if (!this.layout) {
39101             return; // should not happen..
39102         }
39103         var reg = false;
39104         for (var r in this.layout.regions) {
39105             reg = this.layout.getRegion(r);
39106             if (reg.getActivePanel()) {
39107                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39108                 reg.setActivePanel(reg.getActivePanel());
39109                 continue;
39110             }
39111             if (!reg.panels.length) {
39112                 continue;
39113             }
39114             reg.showPanel(reg.getPanel(0));
39115         }
39116         
39117         
39118         
39119         
39120     },
39121     
39122     /**
39123      * Returns the nested BorderLayout for this panel
39124      * @return {Roo.BorderLayout} 
39125      */
39126     getLayout : function(){
39127         return this.layout;
39128     },
39129     
39130      /**
39131      * Adds a xtype elements to the layout of the nested panel
39132      * <pre><code>
39133
39134 panel.addxtype({
39135        xtype : 'ContentPanel',
39136        region: 'west',
39137        items: [ .... ]
39138    }
39139 );
39140
39141 panel.addxtype({
39142         xtype : 'NestedLayoutPanel',
39143         region: 'west',
39144         layout: {
39145            center: { },
39146            west: { }   
39147         },
39148         items : [ ... list of content panels or nested layout panels.. ]
39149    }
39150 );
39151 </code></pre>
39152      * @param {Object} cfg Xtype definition of item to add.
39153      */
39154     addxtype : function(cfg) {
39155         return this.layout.addxtype(cfg);
39156     
39157     }
39158 });/*
39159  * Based on:
39160  * Ext JS Library 1.1.1
39161  * Copyright(c) 2006-2007, Ext JS, LLC.
39162  *
39163  * Originally Released Under LGPL - original licence link has changed is not relivant.
39164  *
39165  * Fork - LGPL
39166  * <script type="text/javascript">
39167  */
39168 /**
39169  * @class Roo.TabPanel
39170  * @extends Roo.util.Observable
39171  * A lightweight tab container.
39172  * <br><br>
39173  * Usage:
39174  * <pre><code>
39175 // basic tabs 1, built from existing content
39176 var tabs = new Roo.TabPanel("tabs1");
39177 tabs.addTab("script", "View Script");
39178 tabs.addTab("markup", "View Markup");
39179 tabs.activate("script");
39180
39181 // more advanced tabs, built from javascript
39182 var jtabs = new Roo.TabPanel("jtabs");
39183 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39184
39185 // set up the UpdateManager
39186 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39187 var updater = tab2.getUpdateManager();
39188 updater.setDefaultUrl("ajax1.htm");
39189 tab2.on('activate', updater.refresh, updater, true);
39190
39191 // Use setUrl for Ajax loading
39192 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39193 tab3.setUrl("ajax2.htm", null, true);
39194
39195 // Disabled tab
39196 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39197 tab4.disable();
39198
39199 jtabs.activate("jtabs-1");
39200  * </code></pre>
39201  * @constructor
39202  * Create a new TabPanel.
39203  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39204  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39205  */
39206 Roo.bootstrap.panel.Tabs = function(config){
39207     /**
39208     * The container element for this TabPanel.
39209     * @type Roo.Element
39210     */
39211     this.el = Roo.get(config.el);
39212     delete config.el;
39213     if(config){
39214         if(typeof config == "boolean"){
39215             this.tabPosition = config ? "bottom" : "top";
39216         }else{
39217             Roo.apply(this, config);
39218         }
39219     }
39220     
39221     if(this.tabPosition == "bottom"){
39222         // if tabs are at the bottom = create the body first.
39223         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39224         this.el.addClass("roo-tabs-bottom");
39225     }
39226     // next create the tabs holders
39227     
39228     if (this.tabPosition == "west"){
39229         
39230         var reg = this.region; // fake it..
39231         while (reg) {
39232             if (!reg.mgr.parent) {
39233                 break;
39234             }
39235             reg = reg.mgr.parent.region;
39236         }
39237         Roo.log("got nest?");
39238         Roo.log(reg);
39239         if (reg.mgr.getRegion('west')) {
39240             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39241             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39242             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39243             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39244             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39245         
39246             
39247         }
39248         
39249         
39250     } else {
39251      
39252         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39253         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39254         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39255         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39256     }
39257     
39258     
39259     if(Roo.isIE){
39260         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39261     }
39262     
39263     // finally - if tabs are at the top, then create the body last..
39264     if(this.tabPosition != "bottom"){
39265         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39266          * @type Roo.Element
39267          */
39268         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39269         this.el.addClass("roo-tabs-top");
39270     }
39271     this.items = [];
39272
39273     this.bodyEl.setStyle("position", "relative");
39274
39275     this.active = null;
39276     this.activateDelegate = this.activate.createDelegate(this);
39277
39278     this.addEvents({
39279         /**
39280          * @event tabchange
39281          * Fires when the active tab changes
39282          * @param {Roo.TabPanel} this
39283          * @param {Roo.TabPanelItem} activePanel The new active tab
39284          */
39285         "tabchange": true,
39286         /**
39287          * @event beforetabchange
39288          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39289          * @param {Roo.TabPanel} this
39290          * @param {Object} e Set cancel to true on this object to cancel the tab change
39291          * @param {Roo.TabPanelItem} tab The tab being changed to
39292          */
39293         "beforetabchange" : true
39294     });
39295
39296     Roo.EventManager.onWindowResize(this.onResize, this);
39297     this.cpad = this.el.getPadding("lr");
39298     this.hiddenCount = 0;
39299
39300
39301     // toolbar on the tabbar support...
39302     if (this.toolbar) {
39303         alert("no toolbar support yet");
39304         this.toolbar  = false;
39305         /*
39306         var tcfg = this.toolbar;
39307         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39308         this.toolbar = new Roo.Toolbar(tcfg);
39309         if (Roo.isSafari) {
39310             var tbl = tcfg.container.child('table', true);
39311             tbl.setAttribute('width', '100%');
39312         }
39313         */
39314         
39315     }
39316    
39317
39318
39319     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39320 };
39321
39322 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39323     /*
39324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39325      */
39326     tabPosition : "top",
39327     /*
39328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39329      */
39330     currentTabWidth : 0,
39331     /*
39332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39333      */
39334     minTabWidth : 40,
39335     /*
39336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39337      */
39338     maxTabWidth : 250,
39339     /*
39340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39341      */
39342     preferredTabWidth : 175,
39343     /*
39344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39345      */
39346     resizeTabs : false,
39347     /*
39348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39349      */
39350     monitorResize : true,
39351     /*
39352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39353      */
39354     toolbar : false,  // set by caller..
39355     
39356     region : false, /// set by caller
39357     
39358     disableTooltips : true, // not used yet...
39359
39360     /**
39361      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39362      * @param {String} id The id of the div to use <b>or create</b>
39363      * @param {String} text The text for the tab
39364      * @param {String} content (optional) Content to put in the TabPanelItem body
39365      * @param {Boolean} closable (optional) True to create a close icon on the tab
39366      * @return {Roo.TabPanelItem} The created TabPanelItem
39367      */
39368     addTab : function(id, text, content, closable, tpl)
39369     {
39370         var item = new Roo.bootstrap.panel.TabItem({
39371             panel: this,
39372             id : id,
39373             text : text,
39374             closable : closable,
39375             tpl : tpl
39376         });
39377         this.addTabItem(item);
39378         if(content){
39379             item.setContent(content);
39380         }
39381         return item;
39382     },
39383
39384     /**
39385      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39386      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39387      * @return {Roo.TabPanelItem}
39388      */
39389     getTab : function(id){
39390         return this.items[id];
39391     },
39392
39393     /**
39394      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39395      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39396      */
39397     hideTab : function(id){
39398         var t = this.items[id];
39399         if(!t.isHidden()){
39400            t.setHidden(true);
39401            this.hiddenCount++;
39402            this.autoSizeTabs();
39403         }
39404     },
39405
39406     /**
39407      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39408      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39409      */
39410     unhideTab : function(id){
39411         var t = this.items[id];
39412         if(t.isHidden()){
39413            t.setHidden(false);
39414            this.hiddenCount--;
39415            this.autoSizeTabs();
39416         }
39417     },
39418
39419     /**
39420      * Adds an existing {@link Roo.TabPanelItem}.
39421      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39422      */
39423     addTabItem : function(item)
39424     {
39425         this.items[item.id] = item;
39426         this.items.push(item);
39427         this.autoSizeTabs();
39428       //  if(this.resizeTabs){
39429     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39430   //         this.autoSizeTabs();
39431 //        }else{
39432 //            item.autoSize();
39433        // }
39434     },
39435
39436     /**
39437      * Removes a {@link Roo.TabPanelItem}.
39438      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39439      */
39440     removeTab : function(id){
39441         var items = this.items;
39442         var tab = items[id];
39443         if(!tab) { return; }
39444         var index = items.indexOf(tab);
39445         if(this.active == tab && items.length > 1){
39446             var newTab = this.getNextAvailable(index);
39447             if(newTab) {
39448                 newTab.activate();
39449             }
39450         }
39451         this.stripEl.dom.removeChild(tab.pnode.dom);
39452         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39453             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39454         }
39455         items.splice(index, 1);
39456         delete this.items[tab.id];
39457         tab.fireEvent("close", tab);
39458         tab.purgeListeners();
39459         this.autoSizeTabs();
39460     },
39461
39462     getNextAvailable : function(start){
39463         var items = this.items;
39464         var index = start;
39465         // look for a next tab that will slide over to
39466         // replace the one being removed
39467         while(index < items.length){
39468             var item = items[++index];
39469             if(item && !item.isHidden()){
39470                 return item;
39471             }
39472         }
39473         // if one isn't found select the previous tab (on the left)
39474         index = start;
39475         while(index >= 0){
39476             var item = items[--index];
39477             if(item && !item.isHidden()){
39478                 return item;
39479             }
39480         }
39481         return null;
39482     },
39483
39484     /**
39485      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39486      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39487      */
39488     disableTab : function(id){
39489         var tab = this.items[id];
39490         if(tab && this.active != tab){
39491             tab.disable();
39492         }
39493     },
39494
39495     /**
39496      * Enables a {@link Roo.TabPanelItem} that is disabled.
39497      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39498      */
39499     enableTab : function(id){
39500         var tab = this.items[id];
39501         tab.enable();
39502     },
39503
39504     /**
39505      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39506      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39507      * @return {Roo.TabPanelItem} The TabPanelItem.
39508      */
39509     activate : function(id)
39510     {
39511         //Roo.log('activite:'  + id);
39512         
39513         var tab = this.items[id];
39514         if(!tab){
39515             return null;
39516         }
39517         if(tab == this.active || tab.disabled){
39518             return tab;
39519         }
39520         var e = {};
39521         this.fireEvent("beforetabchange", this, e, tab);
39522         if(e.cancel !== true && !tab.disabled){
39523             if(this.active){
39524                 this.active.hide();
39525             }
39526             this.active = this.items[id];
39527             this.active.show();
39528             this.fireEvent("tabchange", this, this.active);
39529         }
39530         return tab;
39531     },
39532
39533     /**
39534      * Gets the active {@link Roo.TabPanelItem}.
39535      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39536      */
39537     getActiveTab : function(){
39538         return this.active;
39539     },
39540
39541     /**
39542      * Updates the tab body element to fit the height of the container element
39543      * for overflow scrolling
39544      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39545      */
39546     syncHeight : function(targetHeight){
39547         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39548         var bm = this.bodyEl.getMargins();
39549         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39550         this.bodyEl.setHeight(newHeight);
39551         return newHeight;
39552     },
39553
39554     onResize : function(){
39555         if(this.monitorResize){
39556             this.autoSizeTabs();
39557         }
39558     },
39559
39560     /**
39561      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39562      */
39563     beginUpdate : function(){
39564         this.updating = true;
39565     },
39566
39567     /**
39568      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39569      */
39570     endUpdate : function(){
39571         this.updating = false;
39572         this.autoSizeTabs();
39573     },
39574
39575     /**
39576      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39577      */
39578     autoSizeTabs : function()
39579     {
39580         var count = this.items.length;
39581         var vcount = count - this.hiddenCount;
39582         
39583         if (vcount < 2) {
39584             this.stripEl.hide();
39585         } else {
39586             this.stripEl.show();
39587         }
39588         
39589         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39590             return;
39591         }
39592         
39593         
39594         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39595         var availWidth = Math.floor(w / vcount);
39596         var b = this.stripBody;
39597         if(b.getWidth() > w){
39598             var tabs = this.items;
39599             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39600             if(availWidth < this.minTabWidth){
39601                 /*if(!this.sleft){    // incomplete scrolling code
39602                     this.createScrollButtons();
39603                 }
39604                 this.showScroll();
39605                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39606             }
39607         }else{
39608             if(this.currentTabWidth < this.preferredTabWidth){
39609                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39610             }
39611         }
39612     },
39613
39614     /**
39615      * Returns the number of tabs in this TabPanel.
39616      * @return {Number}
39617      */
39618      getCount : function(){
39619          return this.items.length;
39620      },
39621
39622     /**
39623      * Resizes all the tabs to the passed width
39624      * @param {Number} The new width
39625      */
39626     setTabWidth : function(width){
39627         this.currentTabWidth = width;
39628         for(var i = 0, len = this.items.length; i < len; i++) {
39629                 if(!this.items[i].isHidden()) {
39630                 this.items[i].setWidth(width);
39631             }
39632         }
39633     },
39634
39635     /**
39636      * Destroys this TabPanel
39637      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39638      */
39639     destroy : function(removeEl){
39640         Roo.EventManager.removeResizeListener(this.onResize, this);
39641         for(var i = 0, len = this.items.length; i < len; i++){
39642             this.items[i].purgeListeners();
39643         }
39644         if(removeEl === true){
39645             this.el.update("");
39646             this.el.remove();
39647         }
39648     },
39649     
39650     createStrip : function(container)
39651     {
39652         var strip = document.createElement("nav");
39653         strip.className = Roo.bootstrap.version == 4 ?
39654             "navbar-light bg-light" : 
39655             "navbar navbar-default"; //"x-tabs-wrap";
39656         container.appendChild(strip);
39657         return strip;
39658     },
39659     
39660     createStripList : function(strip)
39661     {
39662         // div wrapper for retard IE
39663         // returns the "tr" element.
39664         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39665         //'<div class="x-tabs-strip-wrap">'+
39666           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39667           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39668         return strip.firstChild; //.firstChild.firstChild.firstChild;
39669     },
39670     createBody : function(container)
39671     {
39672         var body = document.createElement("div");
39673         Roo.id(body, "tab-body");
39674         //Roo.fly(body).addClass("x-tabs-body");
39675         Roo.fly(body).addClass("tab-content");
39676         container.appendChild(body);
39677         return body;
39678     },
39679     createItemBody :function(bodyEl, id){
39680         var body = Roo.getDom(id);
39681         if(!body){
39682             body = document.createElement("div");
39683             body.id = id;
39684         }
39685         //Roo.fly(body).addClass("x-tabs-item-body");
39686         Roo.fly(body).addClass("tab-pane");
39687          bodyEl.insertBefore(body, bodyEl.firstChild);
39688         return body;
39689     },
39690     /** @private */
39691     createStripElements :  function(stripEl, text, closable, tpl)
39692     {
39693         var td = document.createElement("li"); // was td..
39694         td.className = 'nav-item';
39695         
39696         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39697         
39698         
39699         stripEl.appendChild(td);
39700         /*if(closable){
39701             td.className = "x-tabs-closable";
39702             if(!this.closeTpl){
39703                 this.closeTpl = new Roo.Template(
39704                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39705                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39706                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39707                 );
39708             }
39709             var el = this.closeTpl.overwrite(td, {"text": text});
39710             var close = el.getElementsByTagName("div")[0];
39711             var inner = el.getElementsByTagName("em")[0];
39712             return {"el": el, "close": close, "inner": inner};
39713         } else {
39714         */
39715         // not sure what this is..
39716 //            if(!this.tabTpl){
39717                 //this.tabTpl = new Roo.Template(
39718                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39719                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39720                 //);
39721 //                this.tabTpl = new Roo.Template(
39722 //                   '<a href="#">' +
39723 //                   '<span unselectable="on"' +
39724 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39725 //                            ' >{text}</span></a>'
39726 //                );
39727 //                
39728 //            }
39729
39730
39731             var template = tpl || this.tabTpl || false;
39732             
39733             if(!template){
39734                 template =  new Roo.Template(
39735                         Roo.bootstrap.version == 4 ? 
39736                             (
39737                                 '<a class="nav-link" href="#" unselectable="on"' +
39738                                      (this.disableTooltips ? '' : ' title="{text}"') +
39739                                      ' >{text}</a>'
39740                             ) : (
39741                                 '<a class="nav-link" href="#">' +
39742                                 '<span unselectable="on"' +
39743                                          (this.disableTooltips ? '' : ' title="{text}"') +
39744                                     ' >{text}</span></a>'
39745                             )
39746                 );
39747             }
39748             
39749             switch (typeof(template)) {
39750                 case 'object' :
39751                     break;
39752                 case 'string' :
39753                     template = new Roo.Template(template);
39754                     break;
39755                 default :
39756                     break;
39757             }
39758             
39759             var el = template.overwrite(td, {"text": text});
39760             
39761             var inner = el.getElementsByTagName("span")[0];
39762             
39763             return {"el": el, "inner": inner};
39764             
39765     }
39766         
39767     
39768 });
39769
39770 /**
39771  * @class Roo.TabPanelItem
39772  * @extends Roo.util.Observable
39773  * Represents an individual item (tab plus body) in a TabPanel.
39774  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39775  * @param {String} id The id of this TabPanelItem
39776  * @param {String} text The text for the tab of this TabPanelItem
39777  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39778  */
39779 Roo.bootstrap.panel.TabItem = function(config){
39780     /**
39781      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39782      * @type Roo.TabPanel
39783      */
39784     this.tabPanel = config.panel;
39785     /**
39786      * The id for this TabPanelItem
39787      * @type String
39788      */
39789     this.id = config.id;
39790     /** @private */
39791     this.disabled = false;
39792     /** @private */
39793     this.text = config.text;
39794     /** @private */
39795     this.loaded = false;
39796     this.closable = config.closable;
39797
39798     /**
39799      * The body element for this TabPanelItem.
39800      * @type Roo.Element
39801      */
39802     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39803     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39804     this.bodyEl.setStyle("display", "block");
39805     this.bodyEl.setStyle("zoom", "1");
39806     //this.hideAction();
39807
39808     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39809     /** @private */
39810     this.el = Roo.get(els.el);
39811     this.inner = Roo.get(els.inner, true);
39812      this.textEl = Roo.bootstrap.version == 4 ?
39813         this.el : Roo.get(this.el.dom.firstChild, true);
39814
39815     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39816     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39817
39818     
39819 //    this.el.on("mousedown", this.onTabMouseDown, this);
39820     this.el.on("click", this.onTabClick, this);
39821     /** @private */
39822     if(config.closable){
39823         var c = Roo.get(els.close, true);
39824         c.dom.title = this.closeText;
39825         c.addClassOnOver("close-over");
39826         c.on("click", this.closeClick, this);
39827      }
39828
39829     this.addEvents({
39830          /**
39831          * @event activate
39832          * Fires when this tab becomes the active tab.
39833          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39834          * @param {Roo.TabPanelItem} this
39835          */
39836         "activate": true,
39837         /**
39838          * @event beforeclose
39839          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39840          * @param {Roo.TabPanelItem} this
39841          * @param {Object} e Set cancel to true on this object to cancel the close.
39842          */
39843         "beforeclose": true,
39844         /**
39845          * @event close
39846          * Fires when this tab is closed.
39847          * @param {Roo.TabPanelItem} this
39848          */
39849          "close": true,
39850         /**
39851          * @event deactivate
39852          * Fires when this tab is no longer the active tab.
39853          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39854          * @param {Roo.TabPanelItem} this
39855          */
39856          "deactivate" : true
39857     });
39858     this.hidden = false;
39859
39860     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39861 };
39862
39863 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39864            {
39865     purgeListeners : function(){
39866        Roo.util.Observable.prototype.purgeListeners.call(this);
39867        this.el.removeAllListeners();
39868     },
39869     /**
39870      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39871      */
39872     show : function(){
39873         this.status_node.addClass("active");
39874         this.showAction();
39875         if(Roo.isOpera){
39876             this.tabPanel.stripWrap.repaint();
39877         }
39878         this.fireEvent("activate", this.tabPanel, this);
39879     },
39880
39881     /**
39882      * Returns true if this tab is the active tab.
39883      * @return {Boolean}
39884      */
39885     isActive : function(){
39886         return this.tabPanel.getActiveTab() == this;
39887     },
39888
39889     /**
39890      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39891      */
39892     hide : function(){
39893         this.status_node.removeClass("active");
39894         this.hideAction();
39895         this.fireEvent("deactivate", this.tabPanel, this);
39896     },
39897
39898     hideAction : function(){
39899         this.bodyEl.hide();
39900         this.bodyEl.setStyle("position", "absolute");
39901         this.bodyEl.setLeft("-20000px");
39902         this.bodyEl.setTop("-20000px");
39903     },
39904
39905     showAction : function(){
39906         this.bodyEl.setStyle("position", "relative");
39907         this.bodyEl.setTop("");
39908         this.bodyEl.setLeft("");
39909         this.bodyEl.show();
39910     },
39911
39912     /**
39913      * Set the tooltip for the tab.
39914      * @param {String} tooltip The tab's tooltip
39915      */
39916     setTooltip : function(text){
39917         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39918             this.textEl.dom.qtip = text;
39919             this.textEl.dom.removeAttribute('title');
39920         }else{
39921             this.textEl.dom.title = text;
39922         }
39923     },
39924
39925     onTabClick : function(e){
39926         e.preventDefault();
39927         this.tabPanel.activate(this.id);
39928     },
39929
39930     onTabMouseDown : function(e){
39931         e.preventDefault();
39932         this.tabPanel.activate(this.id);
39933     },
39934 /*
39935     getWidth : function(){
39936         return this.inner.getWidth();
39937     },
39938
39939     setWidth : function(width){
39940         var iwidth = width - this.linode.getPadding("lr");
39941         this.inner.setWidth(iwidth);
39942         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39943         this.linode.setWidth(width);
39944     },
39945 */
39946     /**
39947      * Show or hide the tab
39948      * @param {Boolean} hidden True to hide or false to show.
39949      */
39950     setHidden : function(hidden){
39951         this.hidden = hidden;
39952         this.linode.setStyle("display", hidden ? "none" : "");
39953     },
39954
39955     /**
39956      * Returns true if this tab is "hidden"
39957      * @return {Boolean}
39958      */
39959     isHidden : function(){
39960         return this.hidden;
39961     },
39962
39963     /**
39964      * Returns the text for this tab
39965      * @return {String}
39966      */
39967     getText : function(){
39968         return this.text;
39969     },
39970     /*
39971     autoSize : function(){
39972         //this.el.beginMeasure();
39973         this.textEl.setWidth(1);
39974         /*
39975          *  #2804 [new] Tabs in Roojs
39976          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39977          */
39978         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39979         //this.el.endMeasure();
39980     //},
39981
39982     /**
39983      * Sets the text for the tab (Note: this also sets the tooltip text)
39984      * @param {String} text The tab's text and tooltip
39985      */
39986     setText : function(text){
39987         this.text = text;
39988         this.textEl.update(text);
39989         this.setTooltip(text);
39990         //if(!this.tabPanel.resizeTabs){
39991         //    this.autoSize();
39992         //}
39993     },
39994     /**
39995      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39996      */
39997     activate : function(){
39998         this.tabPanel.activate(this.id);
39999     },
40000
40001     /**
40002      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40003      */
40004     disable : function(){
40005         if(this.tabPanel.active != this){
40006             this.disabled = true;
40007             this.status_node.addClass("disabled");
40008         }
40009     },
40010
40011     /**
40012      * Enables this TabPanelItem if it was previously disabled.
40013      */
40014     enable : function(){
40015         this.disabled = false;
40016         this.status_node.removeClass("disabled");
40017     },
40018
40019     /**
40020      * Sets the content for this TabPanelItem.
40021      * @param {String} content The content
40022      * @param {Boolean} loadScripts true to look for and load scripts
40023      */
40024     setContent : function(content, loadScripts){
40025         this.bodyEl.update(content, loadScripts);
40026     },
40027
40028     /**
40029      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40030      * @return {Roo.UpdateManager} The UpdateManager
40031      */
40032     getUpdateManager : function(){
40033         return this.bodyEl.getUpdateManager();
40034     },
40035
40036     /**
40037      * Set a URL to be used to load the content for this TabPanelItem.
40038      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40039      * @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)
40040      * @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)
40041      * @return {Roo.UpdateManager} The UpdateManager
40042      */
40043     setUrl : function(url, params, loadOnce){
40044         if(this.refreshDelegate){
40045             this.un('activate', this.refreshDelegate);
40046         }
40047         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40048         this.on("activate", this.refreshDelegate);
40049         return this.bodyEl.getUpdateManager();
40050     },
40051
40052     /** @private */
40053     _handleRefresh : function(url, params, loadOnce){
40054         if(!loadOnce || !this.loaded){
40055             var updater = this.bodyEl.getUpdateManager();
40056             updater.update(url, params, this._setLoaded.createDelegate(this));
40057         }
40058     },
40059
40060     /**
40061      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40062      *   Will fail silently if the setUrl method has not been called.
40063      *   This does not activate the panel, just updates its content.
40064      */
40065     refresh : function(){
40066         if(this.refreshDelegate){
40067            this.loaded = false;
40068            this.refreshDelegate();
40069         }
40070     },
40071
40072     /** @private */
40073     _setLoaded : function(){
40074         this.loaded = true;
40075     },
40076
40077     /** @private */
40078     closeClick : function(e){
40079         var o = {};
40080         e.stopEvent();
40081         this.fireEvent("beforeclose", this, o);
40082         if(o.cancel !== true){
40083             this.tabPanel.removeTab(this.id);
40084         }
40085     },
40086     /**
40087      * The text displayed in the tooltip for the close icon.
40088      * @type String
40089      */
40090     closeText : "Close this tab"
40091 });
40092 /**
40093 *    This script refer to:
40094 *    Title: International Telephone Input
40095 *    Author: Jack O'Connor
40096 *    Code version:  v12.1.12
40097 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40098 **/
40099
40100 Roo.bootstrap.PhoneInputData = function() {
40101     var d = [
40102       [
40103         "Afghanistan (‫افغانستان‬‎)",
40104         "af",
40105         "93"
40106       ],
40107       [
40108         "Albania (Shqipëri)",
40109         "al",
40110         "355"
40111       ],
40112       [
40113         "Algeria (‫الجزائر‬‎)",
40114         "dz",
40115         "213"
40116       ],
40117       [
40118         "American Samoa",
40119         "as",
40120         "1684"
40121       ],
40122       [
40123         "Andorra",
40124         "ad",
40125         "376"
40126       ],
40127       [
40128         "Angola",
40129         "ao",
40130         "244"
40131       ],
40132       [
40133         "Anguilla",
40134         "ai",
40135         "1264"
40136       ],
40137       [
40138         "Antigua and Barbuda",
40139         "ag",
40140         "1268"
40141       ],
40142       [
40143         "Argentina",
40144         "ar",
40145         "54"
40146       ],
40147       [
40148         "Armenia (Հայաստան)",
40149         "am",
40150         "374"
40151       ],
40152       [
40153         "Aruba",
40154         "aw",
40155         "297"
40156       ],
40157       [
40158         "Australia",
40159         "au",
40160         "61",
40161         0
40162       ],
40163       [
40164         "Austria (Österreich)",
40165         "at",
40166         "43"
40167       ],
40168       [
40169         "Azerbaijan (Azərbaycan)",
40170         "az",
40171         "994"
40172       ],
40173       [
40174         "Bahamas",
40175         "bs",
40176         "1242"
40177       ],
40178       [
40179         "Bahrain (‫البحرين‬‎)",
40180         "bh",
40181         "973"
40182       ],
40183       [
40184         "Bangladesh (বাংলাদেশ)",
40185         "bd",
40186         "880"
40187       ],
40188       [
40189         "Barbados",
40190         "bb",
40191         "1246"
40192       ],
40193       [
40194         "Belarus (Беларусь)",
40195         "by",
40196         "375"
40197       ],
40198       [
40199         "Belgium (België)",
40200         "be",
40201         "32"
40202       ],
40203       [
40204         "Belize",
40205         "bz",
40206         "501"
40207       ],
40208       [
40209         "Benin (Bénin)",
40210         "bj",
40211         "229"
40212       ],
40213       [
40214         "Bermuda",
40215         "bm",
40216         "1441"
40217       ],
40218       [
40219         "Bhutan (འབྲུག)",
40220         "bt",
40221         "975"
40222       ],
40223       [
40224         "Bolivia",
40225         "bo",
40226         "591"
40227       ],
40228       [
40229         "Bosnia and Herzegovina (Босна и Херцеговина)",
40230         "ba",
40231         "387"
40232       ],
40233       [
40234         "Botswana",
40235         "bw",
40236         "267"
40237       ],
40238       [
40239         "Brazil (Brasil)",
40240         "br",
40241         "55"
40242       ],
40243       [
40244         "British Indian Ocean Territory",
40245         "io",
40246         "246"
40247       ],
40248       [
40249         "British Virgin Islands",
40250         "vg",
40251         "1284"
40252       ],
40253       [
40254         "Brunei",
40255         "bn",
40256         "673"
40257       ],
40258       [
40259         "Bulgaria (България)",
40260         "bg",
40261         "359"
40262       ],
40263       [
40264         "Burkina Faso",
40265         "bf",
40266         "226"
40267       ],
40268       [
40269         "Burundi (Uburundi)",
40270         "bi",
40271         "257"
40272       ],
40273       [
40274         "Cambodia (កម្ពុជា)",
40275         "kh",
40276         "855"
40277       ],
40278       [
40279         "Cameroon (Cameroun)",
40280         "cm",
40281         "237"
40282       ],
40283       [
40284         "Canada",
40285         "ca",
40286         "1",
40287         1,
40288         ["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"]
40289       ],
40290       [
40291         "Cape Verde (Kabu Verdi)",
40292         "cv",
40293         "238"
40294       ],
40295       [
40296         "Caribbean Netherlands",
40297         "bq",
40298         "599",
40299         1
40300       ],
40301       [
40302         "Cayman Islands",
40303         "ky",
40304         "1345"
40305       ],
40306       [
40307         "Central African Republic (République centrafricaine)",
40308         "cf",
40309         "236"
40310       ],
40311       [
40312         "Chad (Tchad)",
40313         "td",
40314         "235"
40315       ],
40316       [
40317         "Chile",
40318         "cl",
40319         "56"
40320       ],
40321       [
40322         "China (中国)",
40323         "cn",
40324         "86"
40325       ],
40326       [
40327         "Christmas Island",
40328         "cx",
40329         "61",
40330         2
40331       ],
40332       [
40333         "Cocos (Keeling) Islands",
40334         "cc",
40335         "61",
40336         1
40337       ],
40338       [
40339         "Colombia",
40340         "co",
40341         "57"
40342       ],
40343       [
40344         "Comoros (‫جزر القمر‬‎)",
40345         "km",
40346         "269"
40347       ],
40348       [
40349         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40350         "cd",
40351         "243"
40352       ],
40353       [
40354         "Congo (Republic) (Congo-Brazzaville)",
40355         "cg",
40356         "242"
40357       ],
40358       [
40359         "Cook Islands",
40360         "ck",
40361         "682"
40362       ],
40363       [
40364         "Costa Rica",
40365         "cr",
40366         "506"
40367       ],
40368       [
40369         "Côte d’Ivoire",
40370         "ci",
40371         "225"
40372       ],
40373       [
40374         "Croatia (Hrvatska)",
40375         "hr",
40376         "385"
40377       ],
40378       [
40379         "Cuba",
40380         "cu",
40381         "53"
40382       ],
40383       [
40384         "Curaçao",
40385         "cw",
40386         "599",
40387         0
40388       ],
40389       [
40390         "Cyprus (Κύπρος)",
40391         "cy",
40392         "357"
40393       ],
40394       [
40395         "Czech Republic (Česká republika)",
40396         "cz",
40397         "420"
40398       ],
40399       [
40400         "Denmark (Danmark)",
40401         "dk",
40402         "45"
40403       ],
40404       [
40405         "Djibouti",
40406         "dj",
40407         "253"
40408       ],
40409       [
40410         "Dominica",
40411         "dm",
40412         "1767"
40413       ],
40414       [
40415         "Dominican Republic (República Dominicana)",
40416         "do",
40417         "1",
40418         2,
40419         ["809", "829", "849"]
40420       ],
40421       [
40422         "Ecuador",
40423         "ec",
40424         "593"
40425       ],
40426       [
40427         "Egypt (‫مصر‬‎)",
40428         "eg",
40429         "20"
40430       ],
40431       [
40432         "El Salvador",
40433         "sv",
40434         "503"
40435       ],
40436       [
40437         "Equatorial Guinea (Guinea Ecuatorial)",
40438         "gq",
40439         "240"
40440       ],
40441       [
40442         "Eritrea",
40443         "er",
40444         "291"
40445       ],
40446       [
40447         "Estonia (Eesti)",
40448         "ee",
40449         "372"
40450       ],
40451       [
40452         "Ethiopia",
40453         "et",
40454         "251"
40455       ],
40456       [
40457         "Falkland Islands (Islas Malvinas)",
40458         "fk",
40459         "500"
40460       ],
40461       [
40462         "Faroe Islands (Føroyar)",
40463         "fo",
40464         "298"
40465       ],
40466       [
40467         "Fiji",
40468         "fj",
40469         "679"
40470       ],
40471       [
40472         "Finland (Suomi)",
40473         "fi",
40474         "358",
40475         0
40476       ],
40477       [
40478         "France",
40479         "fr",
40480         "33"
40481       ],
40482       [
40483         "French Guiana (Guyane française)",
40484         "gf",
40485         "594"
40486       ],
40487       [
40488         "French Polynesia (Polynésie française)",
40489         "pf",
40490         "689"
40491       ],
40492       [
40493         "Gabon",
40494         "ga",
40495         "241"
40496       ],
40497       [
40498         "Gambia",
40499         "gm",
40500         "220"
40501       ],
40502       [
40503         "Georgia (საქართველო)",
40504         "ge",
40505         "995"
40506       ],
40507       [
40508         "Germany (Deutschland)",
40509         "de",
40510         "49"
40511       ],
40512       [
40513         "Ghana (Gaana)",
40514         "gh",
40515         "233"
40516       ],
40517       [
40518         "Gibraltar",
40519         "gi",
40520         "350"
40521       ],
40522       [
40523         "Greece (Ελλάδα)",
40524         "gr",
40525         "30"
40526       ],
40527       [
40528         "Greenland (Kalaallit Nunaat)",
40529         "gl",
40530         "299"
40531       ],
40532       [
40533         "Grenada",
40534         "gd",
40535         "1473"
40536       ],
40537       [
40538         "Guadeloupe",
40539         "gp",
40540         "590",
40541         0
40542       ],
40543       [
40544         "Guam",
40545         "gu",
40546         "1671"
40547       ],
40548       [
40549         "Guatemala",
40550         "gt",
40551         "502"
40552       ],
40553       [
40554         "Guernsey",
40555         "gg",
40556         "44",
40557         1
40558       ],
40559       [
40560         "Guinea (Guinée)",
40561         "gn",
40562         "224"
40563       ],
40564       [
40565         "Guinea-Bissau (Guiné Bissau)",
40566         "gw",
40567         "245"
40568       ],
40569       [
40570         "Guyana",
40571         "gy",
40572         "592"
40573       ],
40574       [
40575         "Haiti",
40576         "ht",
40577         "509"
40578       ],
40579       [
40580         "Honduras",
40581         "hn",
40582         "504"
40583       ],
40584       [
40585         "Hong Kong (香港)",
40586         "hk",
40587         "852"
40588       ],
40589       [
40590         "Hungary (Magyarország)",
40591         "hu",
40592         "36"
40593       ],
40594       [
40595         "Iceland (Ísland)",
40596         "is",
40597         "354"
40598       ],
40599       [
40600         "India (भारत)",
40601         "in",
40602         "91"
40603       ],
40604       [
40605         "Indonesia",
40606         "id",
40607         "62"
40608       ],
40609       [
40610         "Iran (‫ایران‬‎)",
40611         "ir",
40612         "98"
40613       ],
40614       [
40615         "Iraq (‫العراق‬‎)",
40616         "iq",
40617         "964"
40618       ],
40619       [
40620         "Ireland",
40621         "ie",
40622         "353"
40623       ],
40624       [
40625         "Isle of Man",
40626         "im",
40627         "44",
40628         2
40629       ],
40630       [
40631         "Israel (‫ישראל‬‎)",
40632         "il",
40633         "972"
40634       ],
40635       [
40636         "Italy (Italia)",
40637         "it",
40638         "39",
40639         0
40640       ],
40641       [
40642         "Jamaica",
40643         "jm",
40644         "1876"
40645       ],
40646       [
40647         "Japan (日本)",
40648         "jp",
40649         "81"
40650       ],
40651       [
40652         "Jersey",
40653         "je",
40654         "44",
40655         3
40656       ],
40657       [
40658         "Jordan (‫الأردن‬‎)",
40659         "jo",
40660         "962"
40661       ],
40662       [
40663         "Kazakhstan (Казахстан)",
40664         "kz",
40665         "7",
40666         1
40667       ],
40668       [
40669         "Kenya",
40670         "ke",
40671         "254"
40672       ],
40673       [
40674         "Kiribati",
40675         "ki",
40676         "686"
40677       ],
40678       [
40679         "Kosovo",
40680         "xk",
40681         "383"
40682       ],
40683       [
40684         "Kuwait (‫الكويت‬‎)",
40685         "kw",
40686         "965"
40687       ],
40688       [
40689         "Kyrgyzstan (Кыргызстан)",
40690         "kg",
40691         "996"
40692       ],
40693       [
40694         "Laos (ລາວ)",
40695         "la",
40696         "856"
40697       ],
40698       [
40699         "Latvia (Latvija)",
40700         "lv",
40701         "371"
40702       ],
40703       [
40704         "Lebanon (‫لبنان‬‎)",
40705         "lb",
40706         "961"
40707       ],
40708       [
40709         "Lesotho",
40710         "ls",
40711         "266"
40712       ],
40713       [
40714         "Liberia",
40715         "lr",
40716         "231"
40717       ],
40718       [
40719         "Libya (‫ليبيا‬‎)",
40720         "ly",
40721         "218"
40722       ],
40723       [
40724         "Liechtenstein",
40725         "li",
40726         "423"
40727       ],
40728       [
40729         "Lithuania (Lietuva)",
40730         "lt",
40731         "370"
40732       ],
40733       [
40734         "Luxembourg",
40735         "lu",
40736         "352"
40737       ],
40738       [
40739         "Macau (澳門)",
40740         "mo",
40741         "853"
40742       ],
40743       [
40744         "Macedonia (FYROM) (Македонија)",
40745         "mk",
40746         "389"
40747       ],
40748       [
40749         "Madagascar (Madagasikara)",
40750         "mg",
40751         "261"
40752       ],
40753       [
40754         "Malawi",
40755         "mw",
40756         "265"
40757       ],
40758       [
40759         "Malaysia",
40760         "my",
40761         "60"
40762       ],
40763       [
40764         "Maldives",
40765         "mv",
40766         "960"
40767       ],
40768       [
40769         "Mali",
40770         "ml",
40771         "223"
40772       ],
40773       [
40774         "Malta",
40775         "mt",
40776         "356"
40777       ],
40778       [
40779         "Marshall Islands",
40780         "mh",
40781         "692"
40782       ],
40783       [
40784         "Martinique",
40785         "mq",
40786         "596"
40787       ],
40788       [
40789         "Mauritania (‫موريتانيا‬‎)",
40790         "mr",
40791         "222"
40792       ],
40793       [
40794         "Mauritius (Moris)",
40795         "mu",
40796         "230"
40797       ],
40798       [
40799         "Mayotte",
40800         "yt",
40801         "262",
40802         1
40803       ],
40804       [
40805         "Mexico (México)",
40806         "mx",
40807         "52"
40808       ],
40809       [
40810         "Micronesia",
40811         "fm",
40812         "691"
40813       ],
40814       [
40815         "Moldova (Republica Moldova)",
40816         "md",
40817         "373"
40818       ],
40819       [
40820         "Monaco",
40821         "mc",
40822         "377"
40823       ],
40824       [
40825         "Mongolia (Монгол)",
40826         "mn",
40827         "976"
40828       ],
40829       [
40830         "Montenegro (Crna Gora)",
40831         "me",
40832         "382"
40833       ],
40834       [
40835         "Montserrat",
40836         "ms",
40837         "1664"
40838       ],
40839       [
40840         "Morocco (‫المغرب‬‎)",
40841         "ma",
40842         "212",
40843         0
40844       ],
40845       [
40846         "Mozambique (Moçambique)",
40847         "mz",
40848         "258"
40849       ],
40850       [
40851         "Myanmar (Burma) (မြန်မာ)",
40852         "mm",
40853         "95"
40854       ],
40855       [
40856         "Namibia (Namibië)",
40857         "na",
40858         "264"
40859       ],
40860       [
40861         "Nauru",
40862         "nr",
40863         "674"
40864       ],
40865       [
40866         "Nepal (नेपाल)",
40867         "np",
40868         "977"
40869       ],
40870       [
40871         "Netherlands (Nederland)",
40872         "nl",
40873         "31"
40874       ],
40875       [
40876         "New Caledonia (Nouvelle-Calédonie)",
40877         "nc",
40878         "687"
40879       ],
40880       [
40881         "New Zealand",
40882         "nz",
40883         "64"
40884       ],
40885       [
40886         "Nicaragua",
40887         "ni",
40888         "505"
40889       ],
40890       [
40891         "Niger (Nijar)",
40892         "ne",
40893         "227"
40894       ],
40895       [
40896         "Nigeria",
40897         "ng",
40898         "234"
40899       ],
40900       [
40901         "Niue",
40902         "nu",
40903         "683"
40904       ],
40905       [
40906         "Norfolk Island",
40907         "nf",
40908         "672"
40909       ],
40910       [
40911         "North Korea (조선 민주주의 인민 공화국)",
40912         "kp",
40913         "850"
40914       ],
40915       [
40916         "Northern Mariana Islands",
40917         "mp",
40918         "1670"
40919       ],
40920       [
40921         "Norway (Norge)",
40922         "no",
40923         "47",
40924         0
40925       ],
40926       [
40927         "Oman (‫عُمان‬‎)",
40928         "om",
40929         "968"
40930       ],
40931       [
40932         "Pakistan (‫پاکستان‬‎)",
40933         "pk",
40934         "92"
40935       ],
40936       [
40937         "Palau",
40938         "pw",
40939         "680"
40940       ],
40941       [
40942         "Palestine (‫فلسطين‬‎)",
40943         "ps",
40944         "970"
40945       ],
40946       [
40947         "Panama (Panamá)",
40948         "pa",
40949         "507"
40950       ],
40951       [
40952         "Papua New Guinea",
40953         "pg",
40954         "675"
40955       ],
40956       [
40957         "Paraguay",
40958         "py",
40959         "595"
40960       ],
40961       [
40962         "Peru (Perú)",
40963         "pe",
40964         "51"
40965       ],
40966       [
40967         "Philippines",
40968         "ph",
40969         "63"
40970       ],
40971       [
40972         "Poland (Polska)",
40973         "pl",
40974         "48"
40975       ],
40976       [
40977         "Portugal",
40978         "pt",
40979         "351"
40980       ],
40981       [
40982         "Puerto Rico",
40983         "pr",
40984         "1",
40985         3,
40986         ["787", "939"]
40987       ],
40988       [
40989         "Qatar (‫قطر‬‎)",
40990         "qa",
40991         "974"
40992       ],
40993       [
40994         "Réunion (La Réunion)",
40995         "re",
40996         "262",
40997         0
40998       ],
40999       [
41000         "Romania (România)",
41001         "ro",
41002         "40"
41003       ],
41004       [
41005         "Russia (Россия)",
41006         "ru",
41007         "7",
41008         0
41009       ],
41010       [
41011         "Rwanda",
41012         "rw",
41013         "250"
41014       ],
41015       [
41016         "Saint Barthélemy",
41017         "bl",
41018         "590",
41019         1
41020       ],
41021       [
41022         "Saint Helena",
41023         "sh",
41024         "290"
41025       ],
41026       [
41027         "Saint Kitts and Nevis",
41028         "kn",
41029         "1869"
41030       ],
41031       [
41032         "Saint Lucia",
41033         "lc",
41034         "1758"
41035       ],
41036       [
41037         "Saint Martin (Saint-Martin (partie française))",
41038         "mf",
41039         "590",
41040         2
41041       ],
41042       [
41043         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41044         "pm",
41045         "508"
41046       ],
41047       [
41048         "Saint Vincent and the Grenadines",
41049         "vc",
41050         "1784"
41051       ],
41052       [
41053         "Samoa",
41054         "ws",
41055         "685"
41056       ],
41057       [
41058         "San Marino",
41059         "sm",
41060         "378"
41061       ],
41062       [
41063         "São Tomé and Príncipe (São Tomé e Príncipe)",
41064         "st",
41065         "239"
41066       ],
41067       [
41068         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41069         "sa",
41070         "966"
41071       ],
41072       [
41073         "Senegal (Sénégal)",
41074         "sn",
41075         "221"
41076       ],
41077       [
41078         "Serbia (Србија)",
41079         "rs",
41080         "381"
41081       ],
41082       [
41083         "Seychelles",
41084         "sc",
41085         "248"
41086       ],
41087       [
41088         "Sierra Leone",
41089         "sl",
41090         "232"
41091       ],
41092       [
41093         "Singapore",
41094         "sg",
41095         "65"
41096       ],
41097       [
41098         "Sint Maarten",
41099         "sx",
41100         "1721"
41101       ],
41102       [
41103         "Slovakia (Slovensko)",
41104         "sk",
41105         "421"
41106       ],
41107       [
41108         "Slovenia (Slovenija)",
41109         "si",
41110         "386"
41111       ],
41112       [
41113         "Solomon Islands",
41114         "sb",
41115         "677"
41116       ],
41117       [
41118         "Somalia (Soomaaliya)",
41119         "so",
41120         "252"
41121       ],
41122       [
41123         "South Africa",
41124         "za",
41125         "27"
41126       ],
41127       [
41128         "South Korea (대한민국)",
41129         "kr",
41130         "82"
41131       ],
41132       [
41133         "South Sudan (‫جنوب السودان‬‎)",
41134         "ss",
41135         "211"
41136       ],
41137       [
41138         "Spain (España)",
41139         "es",
41140         "34"
41141       ],
41142       [
41143         "Sri Lanka (ශ්‍රී ලංකාව)",
41144         "lk",
41145         "94"
41146       ],
41147       [
41148         "Sudan (‫السودان‬‎)",
41149         "sd",
41150         "249"
41151       ],
41152       [
41153         "Suriname",
41154         "sr",
41155         "597"
41156       ],
41157       [
41158         "Svalbard and Jan Mayen",
41159         "sj",
41160         "47",
41161         1
41162       ],
41163       [
41164         "Swaziland",
41165         "sz",
41166         "268"
41167       ],
41168       [
41169         "Sweden (Sverige)",
41170         "se",
41171         "46"
41172       ],
41173       [
41174         "Switzerland (Schweiz)",
41175         "ch",
41176         "41"
41177       ],
41178       [
41179         "Syria (‫سوريا‬‎)",
41180         "sy",
41181         "963"
41182       ],
41183       [
41184         "Taiwan (台灣)",
41185         "tw",
41186         "886"
41187       ],
41188       [
41189         "Tajikistan",
41190         "tj",
41191         "992"
41192       ],
41193       [
41194         "Tanzania",
41195         "tz",
41196         "255"
41197       ],
41198       [
41199         "Thailand (ไทย)",
41200         "th",
41201         "66"
41202       ],
41203       [
41204         "Timor-Leste",
41205         "tl",
41206         "670"
41207       ],
41208       [
41209         "Togo",
41210         "tg",
41211         "228"
41212       ],
41213       [
41214         "Tokelau",
41215         "tk",
41216         "690"
41217       ],
41218       [
41219         "Tonga",
41220         "to",
41221         "676"
41222       ],
41223       [
41224         "Trinidad and Tobago",
41225         "tt",
41226         "1868"
41227       ],
41228       [
41229         "Tunisia (‫تونس‬‎)",
41230         "tn",
41231         "216"
41232       ],
41233       [
41234         "Turkey (Türkiye)",
41235         "tr",
41236         "90"
41237       ],
41238       [
41239         "Turkmenistan",
41240         "tm",
41241         "993"
41242       ],
41243       [
41244         "Turks and Caicos Islands",
41245         "tc",
41246         "1649"
41247       ],
41248       [
41249         "Tuvalu",
41250         "tv",
41251         "688"
41252       ],
41253       [
41254         "U.S. Virgin Islands",
41255         "vi",
41256         "1340"
41257       ],
41258       [
41259         "Uganda",
41260         "ug",
41261         "256"
41262       ],
41263       [
41264         "Ukraine (Україна)",
41265         "ua",
41266         "380"
41267       ],
41268       [
41269         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41270         "ae",
41271         "971"
41272       ],
41273       [
41274         "United Kingdom",
41275         "gb",
41276         "44",
41277         0
41278       ],
41279       [
41280         "United States",
41281         "us",
41282         "1",
41283         0
41284       ],
41285       [
41286         "Uruguay",
41287         "uy",
41288         "598"
41289       ],
41290       [
41291         "Uzbekistan (Oʻzbekiston)",
41292         "uz",
41293         "998"
41294       ],
41295       [
41296         "Vanuatu",
41297         "vu",
41298         "678"
41299       ],
41300       [
41301         "Vatican City (Città del Vaticano)",
41302         "va",
41303         "39",
41304         1
41305       ],
41306       [
41307         "Venezuela",
41308         "ve",
41309         "58"
41310       ],
41311       [
41312         "Vietnam (Việt Nam)",
41313         "vn",
41314         "84"
41315       ],
41316       [
41317         "Wallis and Futuna (Wallis-et-Futuna)",
41318         "wf",
41319         "681"
41320       ],
41321       [
41322         "Western Sahara (‫الصحراء الغربية‬‎)",
41323         "eh",
41324         "212",
41325         1
41326       ],
41327       [
41328         "Yemen (‫اليمن‬‎)",
41329         "ye",
41330         "967"
41331       ],
41332       [
41333         "Zambia",
41334         "zm",
41335         "260"
41336       ],
41337       [
41338         "Zimbabwe",
41339         "zw",
41340         "263"
41341       ],
41342       [
41343         "Åland Islands",
41344         "ax",
41345         "358",
41346         1
41347       ]
41348   ];
41349   
41350   return d;
41351 }/**
41352 *    This script refer to:
41353 *    Title: International Telephone Input
41354 *    Author: Jack O'Connor
41355 *    Code version:  v12.1.12
41356 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41357 **/
41358
41359 /**
41360  * @class Roo.bootstrap.PhoneInput
41361  * @extends Roo.bootstrap.TriggerField
41362  * An input with International dial-code selection
41363  
41364  * @cfg {String} defaultDialCode default '+852'
41365  * @cfg {Array} preferedCountries default []
41366   
41367  * @constructor
41368  * Create a new PhoneInput.
41369  * @param {Object} config Configuration options
41370  */
41371
41372 Roo.bootstrap.PhoneInput = function(config) {
41373     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41374 };
41375
41376 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41377         
41378         listWidth: undefined,
41379         
41380         selectedClass: 'active',
41381         
41382         invalidClass : "has-warning",
41383         
41384         validClass: 'has-success',
41385         
41386         allowed: '0123456789',
41387         
41388         max_length: 15,
41389         
41390         /**
41391          * @cfg {String} defaultDialCode The default dial code when initializing the input
41392          */
41393         defaultDialCode: '+852',
41394         
41395         /**
41396          * @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
41397          */
41398         preferedCountries: false,
41399         
41400         getAutoCreate : function()
41401         {
41402             var data = Roo.bootstrap.PhoneInputData();
41403             var align = this.labelAlign || this.parentLabelAlign();
41404             var id = Roo.id();
41405             
41406             this.allCountries = [];
41407             this.dialCodeMapping = [];
41408             
41409             for (var i = 0; i < data.length; i++) {
41410               var c = data[i];
41411               this.allCountries[i] = {
41412                 name: c[0],
41413                 iso2: c[1],
41414                 dialCode: c[2],
41415                 priority: c[3] || 0,
41416                 areaCodes: c[4] || null
41417               };
41418               this.dialCodeMapping[c[2]] = {
41419                   name: c[0],
41420                   iso2: c[1],
41421                   priority: c[3] || 0,
41422                   areaCodes: c[4] || null
41423               };
41424             }
41425             
41426             var cfg = {
41427                 cls: 'form-group',
41428                 cn: []
41429             };
41430             
41431             var input =  {
41432                 tag: 'input',
41433                 id : id,
41434                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41435                 maxlength: this.max_length,
41436                 cls : 'form-control tel-input',
41437                 autocomplete: 'new-password'
41438             };
41439             
41440             var hiddenInput = {
41441                 tag: 'input',
41442                 type: 'hidden',
41443                 cls: 'hidden-tel-input'
41444             };
41445             
41446             if (this.name) {
41447                 hiddenInput.name = this.name;
41448             }
41449             
41450             if (this.disabled) {
41451                 input.disabled = true;
41452             }
41453             
41454             var flag_container = {
41455                 tag: 'div',
41456                 cls: 'flag-box',
41457                 cn: [
41458                     {
41459                         tag: 'div',
41460                         cls: 'flag'
41461                     },
41462                     {
41463                         tag: 'div',
41464                         cls: 'caret'
41465                     }
41466                 ]
41467             };
41468             
41469             var box = {
41470                 tag: 'div',
41471                 cls: this.hasFeedback ? 'has-feedback' : '',
41472                 cn: [
41473                     hiddenInput,
41474                     input,
41475                     {
41476                         tag: 'input',
41477                         cls: 'dial-code-holder',
41478                         disabled: true
41479                     }
41480                 ]
41481             };
41482             
41483             var container = {
41484                 cls: 'roo-select2-container input-group',
41485                 cn: [
41486                     flag_container,
41487                     box
41488                 ]
41489             };
41490             
41491             if (this.fieldLabel.length) {
41492                 var indicator = {
41493                     tag: 'i',
41494                     tooltip: 'This field is required'
41495                 };
41496                 
41497                 var label = {
41498                     tag: 'label',
41499                     'for':  id,
41500                     cls: 'control-label',
41501                     cn: []
41502                 };
41503                 
41504                 var label_text = {
41505                     tag: 'span',
41506                     html: this.fieldLabel
41507                 };
41508                 
41509                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41510                 label.cn = [
41511                     indicator,
41512                     label_text
41513                 ];
41514                 
41515                 if(this.indicatorpos == 'right') {
41516                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41517                     label.cn = [
41518                         label_text,
41519                         indicator
41520                     ];
41521                 }
41522                 
41523                 if(align == 'left') {
41524                     container = {
41525                         tag: 'div',
41526                         cn: [
41527                             container
41528                         ]
41529                     };
41530                     
41531                     if(this.labelWidth > 12){
41532                         label.style = "width: " + this.labelWidth + 'px';
41533                     }
41534                     if(this.labelWidth < 13 && this.labelmd == 0){
41535                         this.labelmd = this.labelWidth;
41536                     }
41537                     if(this.labellg > 0){
41538                         label.cls += ' col-lg-' + this.labellg;
41539                         input.cls += ' col-lg-' + (12 - this.labellg);
41540                     }
41541                     if(this.labelmd > 0){
41542                         label.cls += ' col-md-' + this.labelmd;
41543                         container.cls += ' col-md-' + (12 - this.labelmd);
41544                     }
41545                     if(this.labelsm > 0){
41546                         label.cls += ' col-sm-' + this.labelsm;
41547                         container.cls += ' col-sm-' + (12 - this.labelsm);
41548                     }
41549                     if(this.labelxs > 0){
41550                         label.cls += ' col-xs-' + this.labelxs;
41551                         container.cls += ' col-xs-' + (12 - this.labelxs);
41552                     }
41553                 }
41554             }
41555             
41556             cfg.cn = [
41557                 label,
41558                 container
41559             ];
41560             
41561             var settings = this;
41562             
41563             ['xs','sm','md','lg'].map(function(size){
41564                 if (settings[size]) {
41565                     cfg.cls += ' col-' + size + '-' + settings[size];
41566                 }
41567             });
41568             
41569             this.store = new Roo.data.Store({
41570                 proxy : new Roo.data.MemoryProxy({}),
41571                 reader : new Roo.data.JsonReader({
41572                     fields : [
41573                         {
41574                             'name' : 'name',
41575                             'type' : 'string'
41576                         },
41577                         {
41578                             'name' : 'iso2',
41579                             'type' : 'string'
41580                         },
41581                         {
41582                             'name' : 'dialCode',
41583                             'type' : 'string'
41584                         },
41585                         {
41586                             'name' : 'priority',
41587                             'type' : 'string'
41588                         },
41589                         {
41590                             'name' : 'areaCodes',
41591                             'type' : 'string'
41592                         }
41593                     ]
41594                 })
41595             });
41596             
41597             if(!this.preferedCountries) {
41598                 this.preferedCountries = [
41599                     'hk',
41600                     'gb',
41601                     'us'
41602                 ];
41603             }
41604             
41605             var p = this.preferedCountries.reverse();
41606             
41607             if(p) {
41608                 for (var i = 0; i < p.length; i++) {
41609                     for (var j = 0; j < this.allCountries.length; j++) {
41610                         if(this.allCountries[j].iso2 == p[i]) {
41611                             var t = this.allCountries[j];
41612                             this.allCountries.splice(j,1);
41613                             this.allCountries.unshift(t);
41614                         }
41615                     } 
41616                 }
41617             }
41618             
41619             this.store.proxy.data = {
41620                 success: true,
41621                 data: this.allCountries
41622             };
41623             
41624             return cfg;
41625         },
41626         
41627         initEvents : function()
41628         {
41629             this.createList();
41630             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41631             
41632             this.indicator = this.indicatorEl();
41633             this.flag = this.flagEl();
41634             this.dialCodeHolder = this.dialCodeHolderEl();
41635             
41636             this.trigger = this.el.select('div.flag-box',true).first();
41637             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41638             
41639             var _this = this;
41640             
41641             (function(){
41642                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41643                 _this.list.setWidth(lw);
41644             }).defer(100);
41645             
41646             this.list.on('mouseover', this.onViewOver, this);
41647             this.list.on('mousemove', this.onViewMove, this);
41648             this.inputEl().on("keyup", this.onKeyUp, this);
41649             this.inputEl().on("keypress", this.onKeyPress, this);
41650             
41651             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41652
41653             this.view = new Roo.View(this.list, this.tpl, {
41654                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41655             });
41656             
41657             this.view.on('click', this.onViewClick, this);
41658             this.setValue(this.defaultDialCode);
41659         },
41660         
41661         onTriggerClick : function(e)
41662         {
41663             Roo.log('trigger click');
41664             if(this.disabled){
41665                 return;
41666             }
41667             
41668             if(this.isExpanded()){
41669                 this.collapse();
41670                 this.hasFocus = false;
41671             }else {
41672                 this.store.load({});
41673                 this.hasFocus = true;
41674                 this.expand();
41675             }
41676         },
41677         
41678         isExpanded : function()
41679         {
41680             return this.list.isVisible();
41681         },
41682         
41683         collapse : function()
41684         {
41685             if(!this.isExpanded()){
41686                 return;
41687             }
41688             this.list.hide();
41689             Roo.get(document).un('mousedown', this.collapseIf, this);
41690             Roo.get(document).un('mousewheel', this.collapseIf, this);
41691             this.fireEvent('collapse', this);
41692             this.validate();
41693         },
41694         
41695         expand : function()
41696         {
41697             Roo.log('expand');
41698
41699             if(this.isExpanded() || !this.hasFocus){
41700                 return;
41701             }
41702             
41703             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41704             this.list.setWidth(lw);
41705             
41706             this.list.show();
41707             this.restrictHeight();
41708             
41709             Roo.get(document).on('mousedown', this.collapseIf, this);
41710             Roo.get(document).on('mousewheel', this.collapseIf, this);
41711             
41712             this.fireEvent('expand', this);
41713         },
41714         
41715         restrictHeight : function()
41716         {
41717             this.list.alignTo(this.inputEl(), this.listAlign);
41718             this.list.alignTo(this.inputEl(), this.listAlign);
41719         },
41720         
41721         onViewOver : function(e, t)
41722         {
41723             if(this.inKeyMode){
41724                 return;
41725             }
41726             var item = this.view.findItemFromChild(t);
41727             
41728             if(item){
41729                 var index = this.view.indexOf(item);
41730                 this.select(index, false);
41731             }
41732         },
41733
41734         // private
41735         onViewClick : function(view, doFocus, el, e)
41736         {
41737             var index = this.view.getSelectedIndexes()[0];
41738             
41739             var r = this.store.getAt(index);
41740             
41741             if(r){
41742                 this.onSelect(r, index);
41743             }
41744             if(doFocus !== false && !this.blockFocus){
41745                 this.inputEl().focus();
41746             }
41747         },
41748         
41749         onViewMove : function(e, t)
41750         {
41751             this.inKeyMode = false;
41752         },
41753         
41754         select : function(index, scrollIntoView)
41755         {
41756             this.selectedIndex = index;
41757             this.view.select(index);
41758             if(scrollIntoView !== false){
41759                 var el = this.view.getNode(index);
41760                 if(el){
41761                     this.list.scrollChildIntoView(el, false);
41762                 }
41763             }
41764         },
41765         
41766         createList : function()
41767         {
41768             this.list = Roo.get(document.body).createChild({
41769                 tag: 'ul',
41770                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41771                 style: 'display:none'
41772             });
41773             
41774             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41775         },
41776         
41777         collapseIf : function(e)
41778         {
41779             var in_combo  = e.within(this.el);
41780             var in_list =  e.within(this.list);
41781             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41782             
41783             if (in_combo || in_list || is_list) {
41784                 return;
41785             }
41786             this.collapse();
41787         },
41788         
41789         onSelect : function(record, index)
41790         {
41791             if(this.fireEvent('beforeselect', this, record, index) !== false){
41792                 
41793                 this.setFlagClass(record.data.iso2);
41794                 this.setDialCode(record.data.dialCode);
41795                 this.hasFocus = false;
41796                 this.collapse();
41797                 this.fireEvent('select', this, record, index);
41798             }
41799         },
41800         
41801         flagEl : function()
41802         {
41803             var flag = this.el.select('div.flag',true).first();
41804             if(!flag){
41805                 return false;
41806             }
41807             return flag;
41808         },
41809         
41810         dialCodeHolderEl : function()
41811         {
41812             var d = this.el.select('input.dial-code-holder',true).first();
41813             if(!d){
41814                 return false;
41815             }
41816             return d;
41817         },
41818         
41819         setDialCode : function(v)
41820         {
41821             this.dialCodeHolder.dom.value = '+'+v;
41822         },
41823         
41824         setFlagClass : function(n)
41825         {
41826             this.flag.dom.className = 'flag '+n;
41827         },
41828         
41829         getValue : function()
41830         {
41831             var v = this.inputEl().getValue();
41832             if(this.dialCodeHolder) {
41833                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41834             }
41835             return v;
41836         },
41837         
41838         setValue : function(v)
41839         {
41840             var d = this.getDialCode(v);
41841             
41842             //invalid dial code
41843             if(v.length == 0 || !d || d.length == 0) {
41844                 if(this.rendered){
41845                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41846                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41847                 }
41848                 return;
41849             }
41850             
41851             //valid dial code
41852             this.setFlagClass(this.dialCodeMapping[d].iso2);
41853             this.setDialCode(d);
41854             this.inputEl().dom.value = v.replace('+'+d,'');
41855             this.hiddenEl().dom.value = this.getValue();
41856             
41857             this.validate();
41858         },
41859         
41860         getDialCode : function(v)
41861         {
41862             v = v ||  '';
41863             
41864             if (v.length == 0) {
41865                 return this.dialCodeHolder.dom.value;
41866             }
41867             
41868             var dialCode = "";
41869             if (v.charAt(0) != "+") {
41870                 return false;
41871             }
41872             var numericChars = "";
41873             for (var i = 1; i < v.length; i++) {
41874               var c = v.charAt(i);
41875               if (!isNaN(c)) {
41876                 numericChars += c;
41877                 if (this.dialCodeMapping[numericChars]) {
41878                   dialCode = v.substr(1, i);
41879                 }
41880                 if (numericChars.length == 4) {
41881                   break;
41882                 }
41883               }
41884             }
41885             return dialCode;
41886         },
41887         
41888         reset : function()
41889         {
41890             this.setValue(this.defaultDialCode);
41891             this.validate();
41892         },
41893         
41894         hiddenEl : function()
41895         {
41896             return this.el.select('input.hidden-tel-input',true).first();
41897         },
41898         
41899         // after setting val
41900         onKeyUp : function(e){
41901             this.setValue(this.getValue());
41902         },
41903         
41904         onKeyPress : function(e){
41905             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41906                 e.stopEvent();
41907             }
41908         }
41909         
41910 });
41911 /**
41912  * @class Roo.bootstrap.MoneyField
41913  * @extends Roo.bootstrap.ComboBox
41914  * Bootstrap MoneyField class
41915  * 
41916  * @constructor
41917  * Create a new MoneyField.
41918  * @param {Object} config Configuration options
41919  */
41920
41921 Roo.bootstrap.MoneyField = function(config) {
41922     
41923     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41924     
41925 };
41926
41927 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41928     
41929     /**
41930      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41931      */
41932     allowDecimals : true,
41933     /**
41934      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41935      */
41936     decimalSeparator : ".",
41937     /**
41938      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41939      */
41940     decimalPrecision : 0,
41941     /**
41942      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41943      */
41944     allowNegative : true,
41945     /**
41946      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41947      */
41948     allowZero: true,
41949     /**
41950      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41951      */
41952     minValue : Number.NEGATIVE_INFINITY,
41953     /**
41954      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41955      */
41956     maxValue : Number.MAX_VALUE,
41957     /**
41958      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41959      */
41960     minText : "The minimum value for this field is {0}",
41961     /**
41962      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41963      */
41964     maxText : "The maximum value for this field is {0}",
41965     /**
41966      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41967      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41968      */
41969     nanText : "{0} is not a valid number",
41970     /**
41971      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41972      */
41973     castInt : true,
41974     /**
41975      * @cfg {String} defaults currency of the MoneyField
41976      * value should be in lkey
41977      */
41978     defaultCurrency : false,
41979     /**
41980      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41981      */
41982     thousandsDelimiter : false,
41983     /**
41984      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41985      */
41986     max_length: false,
41987     
41988     inputlg : 9,
41989     inputmd : 9,
41990     inputsm : 9,
41991     inputxs : 6,
41992     
41993     store : false,
41994     
41995     getAutoCreate : function()
41996     {
41997         var align = this.labelAlign || this.parentLabelAlign();
41998         
41999         var id = Roo.id();
42000
42001         var cfg = {
42002             cls: 'form-group',
42003             cn: []
42004         };
42005
42006         var input =  {
42007             tag: 'input',
42008             id : id,
42009             cls : 'form-control roo-money-amount-input',
42010             autocomplete: 'new-password'
42011         };
42012         
42013         var hiddenInput = {
42014             tag: 'input',
42015             type: 'hidden',
42016             id: Roo.id(),
42017             cls: 'hidden-number-input'
42018         };
42019         
42020         if(this.max_length) {
42021             input.maxlength = this.max_length; 
42022         }
42023         
42024         if (this.name) {
42025             hiddenInput.name = this.name;
42026         }
42027
42028         if (this.disabled) {
42029             input.disabled = true;
42030         }
42031
42032         var clg = 12 - this.inputlg;
42033         var cmd = 12 - this.inputmd;
42034         var csm = 12 - this.inputsm;
42035         var cxs = 12 - this.inputxs;
42036         
42037         var container = {
42038             tag : 'div',
42039             cls : 'row roo-money-field',
42040             cn : [
42041                 {
42042                     tag : 'div',
42043                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42044                     cn : [
42045                         {
42046                             tag : 'div',
42047                             cls: 'roo-select2-container input-group',
42048                             cn: [
42049                                 {
42050                                     tag : 'input',
42051                                     cls : 'form-control roo-money-currency-input',
42052                                     autocomplete: 'new-password',
42053                                     readOnly : 1,
42054                                     name : this.currencyName
42055                                 },
42056                                 {
42057                                     tag :'span',
42058                                     cls : 'input-group-addon',
42059                                     cn : [
42060                                         {
42061                                             tag: 'span',
42062                                             cls: 'caret'
42063                                         }
42064                                     ]
42065                                 }
42066                             ]
42067                         }
42068                     ]
42069                 },
42070                 {
42071                     tag : 'div',
42072                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42073                     cn : [
42074                         {
42075                             tag: 'div',
42076                             cls: this.hasFeedback ? 'has-feedback' : '',
42077                             cn: [
42078                                 input
42079                             ]
42080                         }
42081                     ]
42082                 }
42083             ]
42084             
42085         };
42086         
42087         if (this.fieldLabel.length) {
42088             var indicator = {
42089                 tag: 'i',
42090                 tooltip: 'This field is required'
42091             };
42092
42093             var label = {
42094                 tag: 'label',
42095                 'for':  id,
42096                 cls: 'control-label',
42097                 cn: []
42098             };
42099
42100             var label_text = {
42101                 tag: 'span',
42102                 html: this.fieldLabel
42103             };
42104
42105             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42106             label.cn = [
42107                 indicator,
42108                 label_text
42109             ];
42110
42111             if(this.indicatorpos == 'right') {
42112                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42113                 label.cn = [
42114                     label_text,
42115                     indicator
42116                 ];
42117             }
42118
42119             if(align == 'left') {
42120                 container = {
42121                     tag: 'div',
42122                     cn: [
42123                         container
42124                     ]
42125                 };
42126
42127                 if(this.labelWidth > 12){
42128                     label.style = "width: " + this.labelWidth + 'px';
42129                 }
42130                 if(this.labelWidth < 13 && this.labelmd == 0){
42131                     this.labelmd = this.labelWidth;
42132                 }
42133                 if(this.labellg > 0){
42134                     label.cls += ' col-lg-' + this.labellg;
42135                     input.cls += ' col-lg-' + (12 - this.labellg);
42136                 }
42137                 if(this.labelmd > 0){
42138                     label.cls += ' col-md-' + this.labelmd;
42139                     container.cls += ' col-md-' + (12 - this.labelmd);
42140                 }
42141                 if(this.labelsm > 0){
42142                     label.cls += ' col-sm-' + this.labelsm;
42143                     container.cls += ' col-sm-' + (12 - this.labelsm);
42144                 }
42145                 if(this.labelxs > 0){
42146                     label.cls += ' col-xs-' + this.labelxs;
42147                     container.cls += ' col-xs-' + (12 - this.labelxs);
42148                 }
42149             }
42150         }
42151
42152         cfg.cn = [
42153             label,
42154             container,
42155             hiddenInput
42156         ];
42157         
42158         var settings = this;
42159
42160         ['xs','sm','md','lg'].map(function(size){
42161             if (settings[size]) {
42162                 cfg.cls += ' col-' + size + '-' + settings[size];
42163             }
42164         });
42165         
42166         return cfg;
42167     },
42168     
42169     initEvents : function()
42170     {
42171         this.indicator = this.indicatorEl();
42172         
42173         this.initCurrencyEvent();
42174         
42175         this.initNumberEvent();
42176     },
42177     
42178     initCurrencyEvent : function()
42179     {
42180         if (!this.store) {
42181             throw "can not find store for combo";
42182         }
42183         
42184         this.store = Roo.factory(this.store, Roo.data);
42185         this.store.parent = this;
42186         
42187         this.createList();
42188         
42189         this.triggerEl = this.el.select('.input-group-addon', true).first();
42190         
42191         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42192         
42193         var _this = this;
42194         
42195         (function(){
42196             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42197             _this.list.setWidth(lw);
42198         }).defer(100);
42199         
42200         this.list.on('mouseover', this.onViewOver, this);
42201         this.list.on('mousemove', this.onViewMove, this);
42202         this.list.on('scroll', this.onViewScroll, this);
42203         
42204         if(!this.tpl){
42205             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42206         }
42207         
42208         this.view = new Roo.View(this.list, this.tpl, {
42209             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42210         });
42211         
42212         this.view.on('click', this.onViewClick, this);
42213         
42214         this.store.on('beforeload', this.onBeforeLoad, this);
42215         this.store.on('load', this.onLoad, this);
42216         this.store.on('loadexception', this.onLoadException, this);
42217         
42218         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42219             "up" : function(e){
42220                 this.inKeyMode = true;
42221                 this.selectPrev();
42222             },
42223
42224             "down" : function(e){
42225                 if(!this.isExpanded()){
42226                     this.onTriggerClick();
42227                 }else{
42228                     this.inKeyMode = true;
42229                     this.selectNext();
42230                 }
42231             },
42232
42233             "enter" : function(e){
42234                 this.collapse();
42235                 
42236                 if(this.fireEvent("specialkey", this, e)){
42237                     this.onViewClick(false);
42238                 }
42239                 
42240                 return true;
42241             },
42242
42243             "esc" : function(e){
42244                 this.collapse();
42245             },
42246
42247             "tab" : function(e){
42248                 this.collapse();
42249                 
42250                 if(this.fireEvent("specialkey", this, e)){
42251                     this.onViewClick(false);
42252                 }
42253                 
42254                 return true;
42255             },
42256
42257             scope : this,
42258
42259             doRelay : function(foo, bar, hname){
42260                 if(hname == 'down' || this.scope.isExpanded()){
42261                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42262                 }
42263                 return true;
42264             },
42265
42266             forceKeyDown: true
42267         });
42268         
42269         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42270         
42271     },
42272     
42273     initNumberEvent : function(e)
42274     {
42275         this.inputEl().on("keydown" , this.fireKey,  this);
42276         this.inputEl().on("focus", this.onFocus,  this);
42277         this.inputEl().on("blur", this.onBlur,  this);
42278         
42279         this.inputEl().relayEvent('keyup', this);
42280         
42281         if(this.indicator){
42282             this.indicator.addClass('invisible');
42283         }
42284  
42285         this.originalValue = this.getValue();
42286         
42287         if(this.validationEvent == 'keyup'){
42288             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42289             this.inputEl().on('keyup', this.filterValidation, this);
42290         }
42291         else if(this.validationEvent !== false){
42292             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42293         }
42294         
42295         if(this.selectOnFocus){
42296             this.on("focus", this.preFocus, this);
42297             
42298         }
42299         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42300             this.inputEl().on("keypress", this.filterKeys, this);
42301         } else {
42302             this.inputEl().relayEvent('keypress', this);
42303         }
42304         
42305         var allowed = "0123456789";
42306         
42307         if(this.allowDecimals){
42308             allowed += this.decimalSeparator;
42309         }
42310         
42311         if(this.allowNegative){
42312             allowed += "-";
42313         }
42314         
42315         if(this.thousandsDelimiter) {
42316             allowed += ",";
42317         }
42318         
42319         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42320         
42321         var keyPress = function(e){
42322             
42323             var k = e.getKey();
42324             
42325             var c = e.getCharCode();
42326             
42327             if(
42328                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42329                     allowed.indexOf(String.fromCharCode(c)) === -1
42330             ){
42331                 e.stopEvent();
42332                 return;
42333             }
42334             
42335             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42336                 return;
42337             }
42338             
42339             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42340                 e.stopEvent();
42341             }
42342         };
42343         
42344         this.inputEl().on("keypress", keyPress, this);
42345         
42346     },
42347     
42348     onTriggerClick : function(e)
42349     {   
42350         if(this.disabled){
42351             return;
42352         }
42353         
42354         this.page = 0;
42355         this.loadNext = false;
42356         
42357         if(this.isExpanded()){
42358             this.collapse();
42359             return;
42360         }
42361         
42362         this.hasFocus = true;
42363         
42364         if(this.triggerAction == 'all') {
42365             this.doQuery(this.allQuery, true);
42366             return;
42367         }
42368         
42369         this.doQuery(this.getRawValue());
42370     },
42371     
42372     getCurrency : function()
42373     {   
42374         var v = this.currencyEl().getValue();
42375         
42376         return v;
42377     },
42378     
42379     restrictHeight : function()
42380     {
42381         this.list.alignTo(this.currencyEl(), this.listAlign);
42382         this.list.alignTo(this.currencyEl(), this.listAlign);
42383     },
42384     
42385     onViewClick : function(view, doFocus, el, e)
42386     {
42387         var index = this.view.getSelectedIndexes()[0];
42388         
42389         var r = this.store.getAt(index);
42390         
42391         if(r){
42392             this.onSelect(r, index);
42393         }
42394     },
42395     
42396     onSelect : function(record, index){
42397         
42398         if(this.fireEvent('beforeselect', this, record, index) !== false){
42399         
42400             this.setFromCurrencyData(index > -1 ? record.data : false);
42401             
42402             this.collapse();
42403             
42404             this.fireEvent('select', this, record, index);
42405         }
42406     },
42407     
42408     setFromCurrencyData : function(o)
42409     {
42410         var currency = '';
42411         
42412         this.lastCurrency = o;
42413         
42414         if (this.currencyField) {
42415             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42416         } else {
42417             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42418         }
42419         
42420         this.lastSelectionText = currency;
42421         
42422         //setting default currency
42423         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42424             this.setCurrency(this.defaultCurrency);
42425             return;
42426         }
42427         
42428         this.setCurrency(currency);
42429     },
42430     
42431     setFromData : function(o)
42432     {
42433         var c = {};
42434         
42435         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42436         
42437         this.setFromCurrencyData(c);
42438         
42439         var value = '';
42440         
42441         if (this.name) {
42442             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42443         } else {
42444             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42445         }
42446         
42447         this.setValue(value);
42448         
42449     },
42450     
42451     setCurrency : function(v)
42452     {   
42453         this.currencyValue = v;
42454         
42455         if(this.rendered){
42456             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42457             this.validate();
42458         }
42459     },
42460     
42461     setValue : function(v)
42462     {
42463         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42464         
42465         this.value = v;
42466         
42467         if(this.rendered){
42468             
42469             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42470             
42471             this.inputEl().dom.value = (v == '') ? '' :
42472                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42473             
42474             if(!this.allowZero && v === '0') {
42475                 this.hiddenEl().dom.value = '';
42476                 this.inputEl().dom.value = '';
42477             }
42478             
42479             this.validate();
42480         }
42481     },
42482     
42483     getRawValue : function()
42484     {
42485         var v = this.inputEl().getValue();
42486         
42487         return v;
42488     },
42489     
42490     getValue : function()
42491     {
42492         return this.fixPrecision(this.parseValue(this.getRawValue()));
42493     },
42494     
42495     parseValue : function(value)
42496     {
42497         if(this.thousandsDelimiter) {
42498             value += "";
42499             r = new RegExp(",", "g");
42500             value = value.replace(r, "");
42501         }
42502         
42503         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42504         return isNaN(value) ? '' : value;
42505         
42506     },
42507     
42508     fixPrecision : function(value)
42509     {
42510         if(this.thousandsDelimiter) {
42511             value += "";
42512             r = new RegExp(",", "g");
42513             value = value.replace(r, "");
42514         }
42515         
42516         var nan = isNaN(value);
42517         
42518         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42519             return nan ? '' : value;
42520         }
42521         return parseFloat(value).toFixed(this.decimalPrecision);
42522     },
42523     
42524     decimalPrecisionFcn : function(v)
42525     {
42526         return Math.floor(v);
42527     },
42528     
42529     validateValue : function(value)
42530     {
42531         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42532             return false;
42533         }
42534         
42535         var num = this.parseValue(value);
42536         
42537         if(isNaN(num)){
42538             this.markInvalid(String.format(this.nanText, value));
42539             return false;
42540         }
42541         
42542         if(num < this.minValue){
42543             this.markInvalid(String.format(this.minText, this.minValue));
42544             return false;
42545         }
42546         
42547         if(num > this.maxValue){
42548             this.markInvalid(String.format(this.maxText, this.maxValue));
42549             return false;
42550         }
42551         
42552         return true;
42553     },
42554     
42555     validate : function()
42556     {
42557         if(this.disabled || this.allowBlank){
42558             this.markValid();
42559             return true;
42560         }
42561         
42562         var currency = this.getCurrency();
42563         
42564         if(this.validateValue(this.getRawValue()) && currency.length){
42565             this.markValid();
42566             return true;
42567         }
42568         
42569         this.markInvalid();
42570         return false;
42571     },
42572     
42573     getName: function()
42574     {
42575         return this.name;
42576     },
42577     
42578     beforeBlur : function()
42579     {
42580         if(!this.castInt){
42581             return;
42582         }
42583         
42584         var v = this.parseValue(this.getRawValue());
42585         
42586         if(v || v == 0){
42587             this.setValue(v);
42588         }
42589     },
42590     
42591     onBlur : function()
42592     {
42593         this.beforeBlur();
42594         
42595         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42596             //this.el.removeClass(this.focusClass);
42597         }
42598         
42599         this.hasFocus = false;
42600         
42601         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42602             this.validate();
42603         }
42604         
42605         var v = this.getValue();
42606         
42607         if(String(v) !== String(this.startValue)){
42608             this.fireEvent('change', this, v, this.startValue);
42609         }
42610         
42611         this.fireEvent("blur", this);
42612     },
42613     
42614     inputEl : function()
42615     {
42616         return this.el.select('.roo-money-amount-input', true).first();
42617     },
42618     
42619     currencyEl : function()
42620     {
42621         return this.el.select('.roo-money-currency-input', true).first();
42622     },
42623     
42624     hiddenEl : function()
42625     {
42626         return this.el.select('input.hidden-number-input',true).first();
42627     }
42628     
42629 });/**
42630  * @class Roo.bootstrap.BezierSignature
42631  * @extends Roo.bootstrap.Component
42632  * Bootstrap BezierSignature class
42633  * This script refer to:
42634  *    Title: Signature Pad
42635  *    Author: szimek
42636  *    Availability: https://github.com/szimek/signature_pad
42637  *
42638  * @constructor
42639  * Create a new BezierSignature
42640  * @param {Object} config The config object
42641  */
42642
42643 Roo.bootstrap.BezierSignature = function(config){
42644     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42645     this.addEvents({
42646         "resize" : true
42647     });
42648 };
42649
42650 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42651 {
42652      
42653     curve_data: [],
42654     
42655     is_empty: true,
42656     
42657     mouse_btn_down: true,
42658     
42659     /**
42660      * @cfg {int} canvas height
42661      */
42662     canvas_height: '200px',
42663     
42664     /**
42665      * @cfg {float|function} Radius of a single dot.
42666      */ 
42667     dot_size: false,
42668     
42669     /**
42670      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42671      */
42672     min_width: 0.5,
42673     
42674     /**
42675      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42676      */
42677     max_width: 2.5,
42678     
42679     /**
42680      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42681      */
42682     throttle: 16,
42683     
42684     /**
42685      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42686      */
42687     min_distance: 5,
42688     
42689     /**
42690      * @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.
42691      */
42692     bg_color: 'rgba(0, 0, 0, 0)',
42693     
42694     /**
42695      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42696      */
42697     dot_color: 'black',
42698     
42699     /**
42700      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42701      */ 
42702     velocity_filter_weight: 0.7,
42703     
42704     /**
42705      * @cfg {function} Callback when stroke begin. 
42706      */
42707     onBegin: false,
42708     
42709     /**
42710      * @cfg {function} Callback when stroke end.
42711      */
42712     onEnd: false,
42713     
42714     getAutoCreate : function()
42715     {
42716         var cls = 'roo-signature column';
42717         
42718         if(this.cls){
42719             cls += ' ' + this.cls;
42720         }
42721         
42722         var col_sizes = [
42723             'lg',
42724             'md',
42725             'sm',
42726             'xs'
42727         ];
42728         
42729         for(var i = 0; i < col_sizes.length; i++) {
42730             if(this[col_sizes[i]]) {
42731                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42732             }
42733         }
42734         
42735         var cfg = {
42736             tag: 'div',
42737             cls: cls,
42738             cn: [
42739                 {
42740                     tag: 'div',
42741                     cls: 'roo-signature-body',
42742                     cn: [
42743                         {
42744                             tag: 'canvas',
42745                             cls: 'roo-signature-body-canvas',
42746                             height: this.canvas_height,
42747                             width: this.canvas_width
42748                         }
42749                     ]
42750                 },
42751                 {
42752                     tag: 'input',
42753                     type: 'file',
42754                     style: 'display: none'
42755                 }
42756             ]
42757         };
42758         
42759         return cfg;
42760     },
42761     
42762     initEvents: function() 
42763     {
42764         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42765         
42766         var canvas = this.canvasEl();
42767         
42768         // mouse && touch event swapping...
42769         canvas.dom.style.touchAction = 'none';
42770         canvas.dom.style.msTouchAction = 'none';
42771         
42772         this.mouse_btn_down = false;
42773         canvas.on('mousedown', this._handleMouseDown, this);
42774         canvas.on('mousemove', this._handleMouseMove, this);
42775         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42776         
42777         if (window.PointerEvent) {
42778             canvas.on('pointerdown', this._handleMouseDown, this);
42779             canvas.on('pointermove', this._handleMouseMove, this);
42780             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42781         }
42782         
42783         if ('ontouchstart' in window) {
42784             canvas.on('touchstart', this._handleTouchStart, this);
42785             canvas.on('touchmove', this._handleTouchMove, this);
42786             canvas.on('touchend', this._handleTouchEnd, this);
42787         }
42788         
42789         Roo.EventManager.onWindowResize(this.resize, this, true);
42790         
42791         // file input event
42792         this.fileEl().on('change', this.uploadImage, this);
42793         
42794         this.clear();
42795         
42796         this.resize();
42797     },
42798     
42799     resize: function(){
42800         
42801         var canvas = this.canvasEl().dom;
42802         var ctx = this.canvasElCtx();
42803         var img_data = false;
42804         
42805         if(canvas.width > 0) {
42806             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42807         }
42808         // setting canvas width will clean img data
42809         canvas.width = 0;
42810         
42811         var style = window.getComputedStyle ? 
42812             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42813             
42814         var padding_left = parseInt(style.paddingLeft) || 0;
42815         var padding_right = parseInt(style.paddingRight) || 0;
42816         
42817         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42818         
42819         if(img_data) {
42820             ctx.putImageData(img_data, 0, 0);
42821         }
42822     },
42823     
42824     _handleMouseDown: function(e)
42825     {
42826         if (e.browserEvent.which === 1) {
42827             this.mouse_btn_down = true;
42828             this.strokeBegin(e);
42829         }
42830     },
42831     
42832     _handleMouseMove: function (e)
42833     {
42834         if (this.mouse_btn_down) {
42835             this.strokeMoveUpdate(e);
42836         }
42837     },
42838     
42839     _handleMouseUp: function (e)
42840     {
42841         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42842             this.mouse_btn_down = false;
42843             this.strokeEnd(e);
42844         }
42845     },
42846     
42847     _handleTouchStart: function (e) {
42848         
42849         e.preventDefault();
42850         if (e.browserEvent.targetTouches.length === 1) {
42851             // var touch = e.browserEvent.changedTouches[0];
42852             // this.strokeBegin(touch);
42853             
42854              this.strokeBegin(e); // assume e catching the correct xy...
42855         }
42856     },
42857     
42858     _handleTouchMove: function (e) {
42859         e.preventDefault();
42860         // var touch = event.targetTouches[0];
42861         // _this._strokeMoveUpdate(touch);
42862         this.strokeMoveUpdate(e);
42863     },
42864     
42865     _handleTouchEnd: function (e) {
42866         var wasCanvasTouched = e.target === this.canvasEl().dom;
42867         if (wasCanvasTouched) {
42868             e.preventDefault();
42869             // var touch = event.changedTouches[0];
42870             // _this._strokeEnd(touch);
42871             this.strokeEnd(e);
42872         }
42873     },
42874     
42875     reset: function () {
42876         this._lastPoints = [];
42877         this._lastVelocity = 0;
42878         this._lastWidth = (this.min_width + this.max_width) / 2;
42879         this.canvasElCtx().fillStyle = this.dot_color;
42880     },
42881     
42882     strokeMoveUpdate: function(e)
42883     {
42884         this.strokeUpdate(e);
42885         
42886         if (this.throttle) {
42887             this.throttleStroke(this.strokeUpdate, this.throttle);
42888         }
42889         else {
42890             this.strokeUpdate(e);
42891         }
42892     },
42893     
42894     strokeBegin: function(e)
42895     {
42896         var newPointGroup = {
42897             color: this.dot_color,
42898             points: []
42899         };
42900         
42901         if (typeof this.onBegin === 'function') {
42902             this.onBegin(e);
42903         }
42904         
42905         this.curve_data.push(newPointGroup);
42906         this.reset();
42907         this.strokeUpdate(e);
42908     },
42909     
42910     strokeUpdate: function(e)
42911     {
42912         var rect = this.canvasEl().dom.getBoundingClientRect();
42913         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42914         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42915         var lastPoints = lastPointGroup.points;
42916         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42917         var isLastPointTooClose = lastPoint
42918             ? point.distanceTo(lastPoint) <= this.min_distance
42919             : false;
42920         var color = lastPointGroup.color;
42921         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42922             var curve = this.addPoint(point);
42923             if (!lastPoint) {
42924                 this.drawDot({color: color, point: point});
42925             }
42926             else if (curve) {
42927                 this.drawCurve({color: color, curve: curve});
42928             }
42929             lastPoints.push({
42930                 time: point.time,
42931                 x: point.x,
42932                 y: point.y
42933             });
42934         }
42935     },
42936     
42937     strokeEnd: function(e)
42938     {
42939         this.strokeUpdate(e);
42940         if (typeof this.onEnd === 'function') {
42941             this.onEnd(e);
42942         }
42943     },
42944     
42945     addPoint:  function (point) {
42946         var _lastPoints = this._lastPoints;
42947         _lastPoints.push(point);
42948         if (_lastPoints.length > 2) {
42949             if (_lastPoints.length === 3) {
42950                 _lastPoints.unshift(_lastPoints[0]);
42951             }
42952             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42953             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42954             _lastPoints.shift();
42955             return curve;
42956         }
42957         return null;
42958     },
42959     
42960     calculateCurveWidths: function (startPoint, endPoint) {
42961         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42962             (1 - this.velocity_filter_weight) * this._lastVelocity;
42963
42964         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42965         var widths = {
42966             end: newWidth,
42967             start: this._lastWidth
42968         };
42969         
42970         this._lastVelocity = velocity;
42971         this._lastWidth = newWidth;
42972         return widths;
42973     },
42974     
42975     drawDot: function (_a) {
42976         var color = _a.color, point = _a.point;
42977         var ctx = this.canvasElCtx();
42978         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42979         ctx.beginPath();
42980         this.drawCurveSegment(point.x, point.y, width);
42981         ctx.closePath();
42982         ctx.fillStyle = color;
42983         ctx.fill();
42984     },
42985     
42986     drawCurve: function (_a) {
42987         var color = _a.color, curve = _a.curve;
42988         var ctx = this.canvasElCtx();
42989         var widthDelta = curve.endWidth - curve.startWidth;
42990         var drawSteps = Math.floor(curve.length()) * 2;
42991         ctx.beginPath();
42992         ctx.fillStyle = color;
42993         for (var i = 0; i < drawSteps; i += 1) {
42994         var t = i / drawSteps;
42995         var tt = t * t;
42996         var ttt = tt * t;
42997         var u = 1 - t;
42998         var uu = u * u;
42999         var uuu = uu * u;
43000         var x = uuu * curve.startPoint.x;
43001         x += 3 * uu * t * curve.control1.x;
43002         x += 3 * u * tt * curve.control2.x;
43003         x += ttt * curve.endPoint.x;
43004         var y = uuu * curve.startPoint.y;
43005         y += 3 * uu * t * curve.control1.y;
43006         y += 3 * u * tt * curve.control2.y;
43007         y += ttt * curve.endPoint.y;
43008         var width = curve.startWidth + ttt * widthDelta;
43009         this.drawCurveSegment(x, y, width);
43010         }
43011         ctx.closePath();
43012         ctx.fill();
43013     },
43014     
43015     drawCurveSegment: function (x, y, width) {
43016         var ctx = this.canvasElCtx();
43017         ctx.moveTo(x, y);
43018         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43019         this.is_empty = false;
43020     },
43021     
43022     clear: function()
43023     {
43024         var ctx = this.canvasElCtx();
43025         var canvas = this.canvasEl().dom;
43026         ctx.fillStyle = this.bg_color;
43027         ctx.clearRect(0, 0, canvas.width, canvas.height);
43028         ctx.fillRect(0, 0, canvas.width, canvas.height);
43029         this.curve_data = [];
43030         this.reset();
43031         this.is_empty = true;
43032     },
43033     
43034     fileEl: function()
43035     {
43036         return  this.el.select('input',true).first();
43037     },
43038     
43039     canvasEl: function()
43040     {
43041         return this.el.select('canvas',true).first();
43042     },
43043     
43044     canvasElCtx: function()
43045     {
43046         return this.el.select('canvas',true).first().dom.getContext('2d');
43047     },
43048     
43049     getImage: function(type)
43050     {
43051         if(this.is_empty) {
43052             return false;
43053         }
43054         
43055         // encryption ?
43056         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43057     },
43058     
43059     drawFromImage: function(img_src)
43060     {
43061         var img = new Image();
43062         
43063         img.onload = function(){
43064             this.canvasElCtx().drawImage(img, 0, 0);
43065         }.bind(this);
43066         
43067         img.src = img_src;
43068         
43069         this.is_empty = false;
43070     },
43071     
43072     selectImage: function()
43073     {
43074         this.fileEl().dom.click();
43075     },
43076     
43077     uploadImage: function(e)
43078     {
43079         var reader = new FileReader();
43080         
43081         reader.onload = function(e){
43082             var img = new Image();
43083             img.onload = function(){
43084                 this.reset();
43085                 this.canvasElCtx().drawImage(img, 0, 0);
43086             }.bind(this);
43087             img.src = e.target.result;
43088         }.bind(this);
43089         
43090         reader.readAsDataURL(e.target.files[0]);
43091     },
43092     
43093     // Bezier Point Constructor
43094     Point: (function () {
43095         function Point(x, y, time) {
43096             this.x = x;
43097             this.y = y;
43098             this.time = time || Date.now();
43099         }
43100         Point.prototype.distanceTo = function (start) {
43101             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43102         };
43103         Point.prototype.equals = function (other) {
43104             return this.x === other.x && this.y === other.y && this.time === other.time;
43105         };
43106         Point.prototype.velocityFrom = function (start) {
43107             return this.time !== start.time
43108             ? this.distanceTo(start) / (this.time - start.time)
43109             : 0;
43110         };
43111         return Point;
43112     }()),
43113     
43114     
43115     // Bezier Constructor
43116     Bezier: (function () {
43117         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43118             this.startPoint = startPoint;
43119             this.control2 = control2;
43120             this.control1 = control1;
43121             this.endPoint = endPoint;
43122             this.startWidth = startWidth;
43123             this.endWidth = endWidth;
43124         }
43125         Bezier.fromPoints = function (points, widths, scope) {
43126             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43127             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43128             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43129         };
43130         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43131             var dx1 = s1.x - s2.x;
43132             var dy1 = s1.y - s2.y;
43133             var dx2 = s2.x - s3.x;
43134             var dy2 = s2.y - s3.y;
43135             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43136             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43137             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43138             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43139             var dxm = m1.x - m2.x;
43140             var dym = m1.y - m2.y;
43141             var k = l2 / (l1 + l2);
43142             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43143             var tx = s2.x - cm.x;
43144             var ty = s2.y - cm.y;
43145             return {
43146                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43147                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43148             };
43149         };
43150         Bezier.prototype.length = function () {
43151             var steps = 10;
43152             var length = 0;
43153             var px;
43154             var py;
43155             for (var i = 0; i <= steps; i += 1) {
43156                 var t = i / steps;
43157                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43158                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43159                 if (i > 0) {
43160                     var xdiff = cx - px;
43161                     var ydiff = cy - py;
43162                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43163                 }
43164                 px = cx;
43165                 py = cy;
43166             }
43167             return length;
43168         };
43169         Bezier.prototype.point = function (t, start, c1, c2, end) {
43170             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43171             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43172             + (3.0 * c2 * (1.0 - t) * t * t)
43173             + (end * t * t * t);
43174         };
43175         return Bezier;
43176     }()),
43177     
43178     throttleStroke: function(fn, wait) {
43179       if (wait === void 0) { wait = 250; }
43180       var previous = 0;
43181       var timeout = null;
43182       var result;
43183       var storedContext;
43184       var storedArgs;
43185       var later = function () {
43186           previous = Date.now();
43187           timeout = null;
43188           result = fn.apply(storedContext, storedArgs);
43189           if (!timeout) {
43190               storedContext = null;
43191               storedArgs = [];
43192           }
43193       };
43194       return function wrapper() {
43195           var args = [];
43196           for (var _i = 0; _i < arguments.length; _i++) {
43197               args[_i] = arguments[_i];
43198           }
43199           var now = Date.now();
43200           var remaining = wait - (now - previous);
43201           storedContext = this;
43202           storedArgs = args;
43203           if (remaining <= 0 || remaining > wait) {
43204               if (timeout) {
43205                   clearTimeout(timeout);
43206                   timeout = null;
43207               }
43208               previous = now;
43209               result = fn.apply(storedContext, storedArgs);
43210               if (!timeout) {
43211                   storedContext = null;
43212                   storedArgs = [];
43213               }
43214           }
43215           else if (!timeout) {
43216               timeout = window.setTimeout(later, remaining);
43217           }
43218           return result;
43219       };
43220   }
43221   
43222 });
43223
43224  
43225
43226