6d77007138d8867a97b0892a74804ea53af414d8
[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             cards.push(this.items[i]);
2285             if (ctarget < 0 && pos == 'above') {
2286                 ctarget = i > 0 ? i - 1 : 0;
2287                 cpos = i > 0 ? 'below' : pos;
2288             }
2289         }
2290         if (!cards.length) {
2291             return [ true, 'below' ];
2292         }
2293         
2294         if (ctarget < 0) {
2295             ctarget = cards.length -1;
2296             cpos = 'below';
2297         }
2298         if (cards[ctarget].el == dragged_card_el) {
2299             return false;
2300         }
2301         
2302         if (cpos == 'below') {
2303             var card_after = ctarget+1 == cards.length ? false : cards[ctarget+1];
2304             
2305             // then above should not be dragged_card_el.
2306             // and ctarget sho
2307             
2308             if (card_after  && card_after.el == dragged_card_el) {
2309                 return false;
2310             }
2311             return [ cards[ctarget], cpos ];
2312         }
2313         
2314         // its's after ..
2315         var card_before = ctarget > 0 ? cards[ctarget-1] : false;
2316         
2317             
2318         if (card_before  && card_before.el == dragged_card_el) {
2319             return false;
2320         }
2321         
2322         return [ cards[ctarget], cpos, cards, ctarget ];
2323     },
2324     
2325     onNodeEnter : function(n, dd, e, data){
2326         return false;
2327     },
2328     onNodeOver : function(n, dd, e, data)
2329     {
2330        
2331         var target_info = this.getTargetFromEvent(e,data.source.el);
2332         if (target_info === false) {
2333             this.dropPlaceHolder('hide');
2334             return false;
2335         }
2336         Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2337         
2338          
2339         this.dropPlaceHolder('show', target_info,data);
2340         
2341         return false; 
2342     },
2343     onNodeOut : function(n, dd, e, data){
2344         this.dropPlaceHolder('hide');
2345      
2346     },
2347     onNodeDrop : function(n, dd, e, data)
2348     {
2349         
2350         // call drop - return false if  
2351         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2352             return false;
2353         }
2354         
2355         var target_info = this.getTargetFromEvent(e,data.source.el);
2356         if (target_info === false) {
2357             return false;
2358         }
2359         
2360         var pt = this.getDropPoint(e, n, dd);
2361         var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2362         if (pt == "below") {
2363             insertAt++;
2364         }
2365         for (var i = 0; i < this.items.length; i++) {
2366             var r = this.items[i];
2367             //var dup = this.store.getById(r.id);
2368             if (dup && (dd != this.dragZone)) {
2369                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2370             } else {
2371             if (data.copy) {
2372                 this.store.insert(insertAt++, r.copy());
2373             } else {
2374                 data.source.isDirtyFlag = true;
2375                 r.store.remove(r);
2376                 this.store.insert(insertAt++, r);
2377             }
2378             this.isDirtyFlag = true;
2379             }
2380         }
2381         this.dragZone.cachedTarget = null;
2382         return true;
2383     },
2384     
2385     /**    Decide whether to drop above or below a View node. */
2386     getDropPoint : function(e, n, dd)
2387     {
2388         if (dd) {
2389              return false;
2390         }
2391         if (n == this.bodyEl.dom) {
2392             return "above";
2393         }
2394         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2395         var c = t + (b - t) / 2;
2396         var y = Roo.lib.Event.getPageY(e);
2397         if(y <= c) {
2398             return "above";
2399         }else{
2400             return "below";
2401         }
2402     },
2403     onToggleCollapse : function(e)
2404         {
2405         if (this.collapsed) {
2406             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2407             this.el.select('.roo-collapsable').addClass('show');
2408             this.collapsed = false;
2409             return;
2410         }
2411         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2412         this.el.select('.roo-collapsable').removeClass('show');
2413         this.collapsed = true;
2414         
2415     
2416     },
2417     dropPlaceHolder: function (action, where_ar, data)
2418     {
2419         if (this.dropEl === false) {
2420             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2421             cls : 'd-none'
2422             },true);
2423         }
2424         this.dropEl.removeClass(['d-none', 'd-block']);        
2425         if (action == 'hide') {
2426             
2427             this.dropEl.addClass('d-none');
2428             return;
2429         }
2430         var cardel = where_ar[0].el.dom;
2431         
2432         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2433         if (where_ar[1] == 'above') {
2434             cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2435         } else if (cardel.nextSibling) {
2436             cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2437         } else {
2438             cardel.parentNode.append(this.dropEl.dom);
2439         }
2440         this.dropEl.addClass('d-block roo-card-dropzone');
2441         
2442         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2443         
2444         
2445     
2446     
2447     
2448     }
2449
2450     
2451 });
2452
2453 /*
2454  * - LGPL
2455  *
2456  * Card header - holder for the card header elements.
2457  * 
2458  */
2459
2460 /**
2461  * @class Roo.bootstrap.CardHeader
2462  * @extends Roo.bootstrap.Element
2463  * Bootstrap CardHeader class
2464  * @constructor
2465  * Create a new Card Header - that you can embed children into
2466  * @param {Object} config The config object
2467  */
2468
2469 Roo.bootstrap.CardHeader = function(config){
2470     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2474     
2475     
2476     container_method : 'getCardHeader' 
2477     
2478      
2479     
2480     
2481    
2482 });
2483
2484  
2485
2486  /*
2487  * - LGPL
2488  *
2489  * Card header - holder for the card header elements.
2490  * 
2491  */
2492
2493 /**
2494  * @class Roo.bootstrap.CardImageTop
2495  * @extends Roo.bootstrap.Element
2496  * Bootstrap CardImageTop class
2497  * @constructor
2498  * Create a new Card Image Top container
2499  * @param {Object} config The config object
2500  */
2501
2502 Roo.bootstrap.CardImageTop = function(config){
2503     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2504 };
2505
2506 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2507     
2508    
2509     container_method : 'getCardImageTop' 
2510     
2511      
2512     
2513    
2514 });
2515
2516  
2517
2518  /*
2519  * - LGPL
2520  *
2521  * image
2522  * 
2523  */
2524
2525
2526 /**
2527  * @class Roo.bootstrap.Img
2528  * @extends Roo.bootstrap.Component
2529  * Bootstrap Img class
2530  * @cfg {Boolean} imgResponsive false | true
2531  * @cfg {String} border rounded | circle | thumbnail
2532  * @cfg {String} src image source
2533  * @cfg {String} alt image alternative text
2534  * @cfg {String} href a tag href
2535  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2536  * @cfg {String} xsUrl xs image source
2537  * @cfg {String} smUrl sm image source
2538  * @cfg {String} mdUrl md image source
2539  * @cfg {String} lgUrl lg image source
2540  * 
2541  * @constructor
2542  * Create a new Input
2543  * @param {Object} config The config object
2544  */
2545
2546 Roo.bootstrap.Img = function(config){
2547     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2548     
2549     this.addEvents({
2550         // img events
2551         /**
2552          * @event click
2553          * The img click event for the img.
2554          * @param {Roo.EventObject} e
2555          */
2556         "click" : true
2557     });
2558 };
2559
2560 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2561     
2562     imgResponsive: true,
2563     border: '',
2564     src: 'about:blank',
2565     href: false,
2566     target: false,
2567     xsUrl: '',
2568     smUrl: '',
2569     mdUrl: '',
2570     lgUrl: '',
2571
2572     getAutoCreate : function()
2573     {   
2574         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2575             return this.createSingleImg();
2576         }
2577         
2578         var cfg = {
2579             tag: 'div',
2580             cls: 'roo-image-responsive-group',
2581             cn: []
2582         };
2583         var _this = this;
2584         
2585         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2586             
2587             if(!_this[size + 'Url']){
2588                 return;
2589             }
2590             
2591             var img = {
2592                 tag: 'img',
2593                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2594                 html: _this.html || cfg.html,
2595                 src: _this[size + 'Url']
2596             };
2597             
2598             img.cls += ' roo-image-responsive-' + size;
2599             
2600             var s = ['xs', 'sm', 'md', 'lg'];
2601             
2602             s.splice(s.indexOf(size), 1);
2603             
2604             Roo.each(s, function(ss){
2605                 img.cls += ' hidden-' + ss;
2606             });
2607             
2608             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2609                 cfg.cls += ' img-' + _this.border;
2610             }
2611             
2612             if(_this.alt){
2613                 cfg.alt = _this.alt;
2614             }
2615             
2616             if(_this.href){
2617                 var a = {
2618                     tag: 'a',
2619                     href: _this.href,
2620                     cn: [
2621                         img
2622                     ]
2623                 };
2624
2625                 if(this.target){
2626                     a.target = _this.target;
2627                 }
2628             }
2629             
2630             cfg.cn.push((_this.href) ? a : img);
2631             
2632         });
2633         
2634         return cfg;
2635     },
2636     
2637     createSingleImg : function()
2638     {
2639         var cfg = {
2640             tag: 'img',
2641             cls: (this.imgResponsive) ? 'img-responsive' : '',
2642             html : null,
2643             src : 'about:blank'  // just incase src get's set to undefined?!?
2644         };
2645         
2646         cfg.html = this.html || cfg.html;
2647         
2648         cfg.src = this.src || cfg.src;
2649         
2650         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2651             cfg.cls += ' img-' + this.border;
2652         }
2653         
2654         if(this.alt){
2655             cfg.alt = this.alt;
2656         }
2657         
2658         if(this.href){
2659             var a = {
2660                 tag: 'a',
2661                 href: this.href,
2662                 cn: [
2663                     cfg
2664                 ]
2665             };
2666             
2667             if(this.target){
2668                 a.target = this.target;
2669             }
2670             
2671         }
2672         
2673         return (this.href) ? a : cfg;
2674     },
2675     
2676     initEvents: function() 
2677     {
2678         if(!this.href){
2679             this.el.on('click', this.onClick, this);
2680         }
2681         
2682     },
2683     
2684     onClick : function(e)
2685     {
2686         Roo.log('img onclick');
2687         this.fireEvent('click', this, e);
2688     },
2689     /**
2690      * Sets the url of the image - used to update it
2691      * @param {String} url the url of the image
2692      */
2693     
2694     setSrc : function(url)
2695     {
2696         this.src =  url;
2697         
2698         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2699             this.el.dom.src =  url;
2700             return;
2701         }
2702         
2703         this.el.select('img', true).first().dom.src =  url;
2704     }
2705     
2706     
2707    
2708 });
2709
2710  /*
2711  * - LGPL
2712  *
2713  * image
2714  * 
2715  */
2716
2717
2718 /**
2719  * @class Roo.bootstrap.Link
2720  * @extends Roo.bootstrap.Component
2721  * Bootstrap Link Class
2722  * @cfg {String} alt image alternative text
2723  * @cfg {String} href a tag href
2724  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2725  * @cfg {String} html the content of the link.
2726  * @cfg {String} anchor name for the anchor link
2727  * @cfg {String} fa - favicon
2728
2729  * @cfg {Boolean} preventDefault (true | false) default false
2730
2731  * 
2732  * @constructor
2733  * Create a new Input
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.Link = function(config){
2738     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2739     
2740     this.addEvents({
2741         // img events
2742         /**
2743          * @event click
2744          * The img click event for the img.
2745          * @param {Roo.EventObject} e
2746          */
2747         "click" : true
2748     });
2749 };
2750
2751 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2752     
2753     href: false,
2754     target: false,
2755     preventDefault: false,
2756     anchor : false,
2757     alt : false,
2758     fa: false,
2759
2760
2761     getAutoCreate : function()
2762     {
2763         var html = this.html || '';
2764         
2765         if (this.fa !== false) {
2766             html = '<i class="fa fa-' + this.fa + '"></i>';
2767         }
2768         var cfg = {
2769             tag: 'a'
2770         };
2771         // anchor's do not require html/href...
2772         if (this.anchor === false) {
2773             cfg.html = html;
2774             cfg.href = this.href || '#';
2775         } else {
2776             cfg.name = this.anchor;
2777             if (this.html !== false || this.fa !== false) {
2778                 cfg.html = html;
2779             }
2780             if (this.href !== false) {
2781                 cfg.href = this.href;
2782             }
2783         }
2784         
2785         if(this.alt !== false){
2786             cfg.alt = this.alt;
2787         }
2788         
2789         
2790         if(this.target !== false) {
2791             cfg.target = this.target;
2792         }
2793         
2794         return cfg;
2795     },
2796     
2797     initEvents: function() {
2798         
2799         if(!this.href || this.preventDefault){
2800             this.el.on('click', this.onClick, this);
2801         }
2802     },
2803     
2804     onClick : function(e)
2805     {
2806         if(this.preventDefault){
2807             e.preventDefault();
2808         }
2809         //Roo.log('img onclick');
2810         this.fireEvent('click', this, e);
2811     }
2812    
2813 });
2814
2815  /*
2816  * - LGPL
2817  *
2818  * header
2819  * 
2820  */
2821
2822 /**
2823  * @class Roo.bootstrap.Header
2824  * @extends Roo.bootstrap.Component
2825  * Bootstrap Header class
2826  * @cfg {String} html content of header
2827  * @cfg {Number} level (1|2|3|4|5|6) default 1
2828  * 
2829  * @constructor
2830  * Create a new Header
2831  * @param {Object} config The config object
2832  */
2833
2834
2835 Roo.bootstrap.Header  = function(config){
2836     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2837 };
2838
2839 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2840     
2841     //href : false,
2842     html : false,
2843     level : 1,
2844     
2845     
2846     
2847     getAutoCreate : function(){
2848         
2849         
2850         
2851         var cfg = {
2852             tag: 'h' + (1 *this.level),
2853             html: this.html || ''
2854         } ;
2855         
2856         return cfg;
2857     }
2858    
2859 });
2860
2861  
2862
2863  /*
2864  * Based on:
2865  * Ext JS Library 1.1.1
2866  * Copyright(c) 2006-2007, Ext JS, LLC.
2867  *
2868  * Originally Released Under LGPL - original licence link has changed is not relivant.
2869  *
2870  * Fork - LGPL
2871  * <script type="text/javascript">
2872  */
2873  
2874 /**
2875  * @class Roo.bootstrap.MenuMgr
2876  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2877  * @singleton
2878  */
2879 Roo.bootstrap.MenuMgr = function(){
2880    var menus, active, groups = {}, attached = false, lastShow = new Date();
2881
2882    // private - called when first menu is created
2883    function init(){
2884        menus = {};
2885        active = new Roo.util.MixedCollection();
2886        Roo.get(document).addKeyListener(27, function(){
2887            if(active.length > 0){
2888                hideAll();
2889            }
2890        });
2891    }
2892
2893    // private
2894    function hideAll(){
2895        if(active && active.length > 0){
2896            var c = active.clone();
2897            c.each(function(m){
2898                m.hide();
2899            });
2900        }
2901    }
2902
2903    // private
2904    function onHide(m){
2905        active.remove(m);
2906        if(active.length < 1){
2907            Roo.get(document).un("mouseup", onMouseDown);
2908             
2909            attached = false;
2910        }
2911    }
2912
2913    // private
2914    function onShow(m){
2915        var last = active.last();
2916        lastShow = new Date();
2917        active.add(m);
2918        if(!attached){
2919           Roo.get(document).on("mouseup", onMouseDown);
2920            
2921            attached = true;
2922        }
2923        if(m.parentMenu){
2924           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2925           m.parentMenu.activeChild = m;
2926        }else if(last && last.isVisible()){
2927           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2928        }
2929    }
2930
2931    // private
2932    function onBeforeHide(m){
2933        if(m.activeChild){
2934            m.activeChild.hide();
2935        }
2936        if(m.autoHideTimer){
2937            clearTimeout(m.autoHideTimer);
2938            delete m.autoHideTimer;
2939        }
2940    }
2941
2942    // private
2943    function onBeforeShow(m){
2944        var pm = m.parentMenu;
2945        if(!pm && !m.allowOtherMenus){
2946            hideAll();
2947        }else if(pm && pm.activeChild && active != m){
2948            pm.activeChild.hide();
2949        }
2950    }
2951
2952    // private this should really trigger on mouseup..
2953    function onMouseDown(e){
2954         Roo.log("on Mouse Up");
2955         
2956         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2957             Roo.log("MenuManager hideAll");
2958             hideAll();
2959             e.stopEvent();
2960         }
2961         
2962         
2963    }
2964
2965    // private
2966    function onBeforeCheck(mi, state){
2967        if(state){
2968            var g = groups[mi.group];
2969            for(var i = 0, l = g.length; i < l; i++){
2970                if(g[i] != mi){
2971                    g[i].setChecked(false);
2972                }
2973            }
2974        }
2975    }
2976
2977    return {
2978
2979        /**
2980         * Hides all menus that are currently visible
2981         */
2982        hideAll : function(){
2983             hideAll();  
2984        },
2985
2986        // private
2987        register : function(menu){
2988            if(!menus){
2989                init();
2990            }
2991            menus[menu.id] = menu;
2992            menu.on("beforehide", onBeforeHide);
2993            menu.on("hide", onHide);
2994            menu.on("beforeshow", onBeforeShow);
2995            menu.on("show", onShow);
2996            var g = menu.group;
2997            if(g && menu.events["checkchange"]){
2998                if(!groups[g]){
2999                    groups[g] = [];
3000                }
3001                groups[g].push(menu);
3002                menu.on("checkchange", onCheck);
3003            }
3004        },
3005
3006         /**
3007          * Returns a {@link Roo.menu.Menu} object
3008          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3009          * be used to generate and return a new Menu instance.
3010          */
3011        get : function(menu){
3012            if(typeof menu == "string"){ // menu id
3013                return menus[menu];
3014            }else if(menu.events){  // menu instance
3015                return menu;
3016            }
3017            /*else if(typeof menu.length == 'number'){ // array of menu items?
3018                return new Roo.bootstrap.Menu({items:menu});
3019            }else{ // otherwise, must be a config
3020                return new Roo.bootstrap.Menu(menu);
3021            }
3022            */
3023            return false;
3024        },
3025
3026        // private
3027        unregister : function(menu){
3028            delete menus[menu.id];
3029            menu.un("beforehide", onBeforeHide);
3030            menu.un("hide", onHide);
3031            menu.un("beforeshow", onBeforeShow);
3032            menu.un("show", onShow);
3033            var g = menu.group;
3034            if(g && menu.events["checkchange"]){
3035                groups[g].remove(menu);
3036                menu.un("checkchange", onCheck);
3037            }
3038        },
3039
3040        // private
3041        registerCheckable : function(menuItem){
3042            var g = menuItem.group;
3043            if(g){
3044                if(!groups[g]){
3045                    groups[g] = [];
3046                }
3047                groups[g].push(menuItem);
3048                menuItem.on("beforecheckchange", onBeforeCheck);
3049            }
3050        },
3051
3052        // private
3053        unregisterCheckable : function(menuItem){
3054            var g = menuItem.group;
3055            if(g){
3056                groups[g].remove(menuItem);
3057                menuItem.un("beforecheckchange", onBeforeCheck);
3058            }
3059        }
3060    };
3061 }();/*
3062  * - LGPL
3063  *
3064  * menu
3065  * 
3066  */
3067
3068 /**
3069  * @class Roo.bootstrap.Menu
3070  * @extends Roo.bootstrap.Component
3071  * Bootstrap Menu class - container for MenuItems
3072  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3073  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3074  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3075  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3076  * 
3077  * @constructor
3078  * Create a new Menu
3079  * @param {Object} config The config object
3080  */
3081
3082
3083 Roo.bootstrap.Menu = function(config){
3084     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3085     if (this.registerMenu && this.type != 'treeview')  {
3086         Roo.bootstrap.MenuMgr.register(this);
3087     }
3088     
3089     
3090     this.addEvents({
3091         /**
3092          * @event beforeshow
3093          * Fires before this menu is displayed (return false to block)
3094          * @param {Roo.menu.Menu} this
3095          */
3096         beforeshow : true,
3097         /**
3098          * @event beforehide
3099          * Fires before this menu is hidden (return false to block)
3100          * @param {Roo.menu.Menu} this
3101          */
3102         beforehide : true,
3103         /**
3104          * @event show
3105          * Fires after this menu is displayed
3106          * @param {Roo.menu.Menu} this
3107          */
3108         show : true,
3109         /**
3110          * @event hide
3111          * Fires after this menu is hidden
3112          * @param {Roo.menu.Menu} this
3113          */
3114         hide : true,
3115         /**
3116          * @event click
3117          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3118          * @param {Roo.menu.Menu} this
3119          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3120          * @param {Roo.EventObject} e
3121          */
3122         click : true,
3123         /**
3124          * @event mouseover
3125          * Fires when the mouse is hovering over this menu
3126          * @param {Roo.menu.Menu} this
3127          * @param {Roo.EventObject} e
3128          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3129          */
3130         mouseover : true,
3131         /**
3132          * @event mouseout
3133          * Fires when the mouse exits this menu
3134          * @param {Roo.menu.Menu} this
3135          * @param {Roo.EventObject} e
3136          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3137          */
3138         mouseout : true,
3139         /**
3140          * @event itemclick
3141          * Fires when a menu item contained in this menu is clicked
3142          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3143          * @param {Roo.EventObject} e
3144          */
3145         itemclick: true
3146     });
3147     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3148 };
3149
3150 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3151     
3152    /// html : false,
3153     //align : '',
3154     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3155     type: false,
3156     /**
3157      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3158      */
3159     registerMenu : true,
3160     
3161     menuItems :false, // stores the menu items..
3162     
3163     hidden:true,
3164         
3165     parentMenu : false,
3166     
3167     stopEvent : true,
3168     
3169     isLink : false,
3170     
3171     getChildContainer : function() {
3172         return this.el;  
3173     },
3174     
3175     getAutoCreate : function(){
3176          
3177         //if (['right'].indexOf(this.align)!==-1) {
3178         //    cfg.cn[1].cls += ' pull-right'
3179         //}
3180         
3181         
3182         var cfg = {
3183             tag : 'ul',
3184             cls : 'dropdown-menu' ,
3185             style : 'z-index:1000'
3186             
3187         };
3188         
3189         if (this.type === 'submenu') {
3190             cfg.cls = 'submenu active';
3191         }
3192         if (this.type === 'treeview') {
3193             cfg.cls = 'treeview-menu';
3194         }
3195         
3196         return cfg;
3197     },
3198     initEvents : function() {
3199         
3200        // Roo.log("ADD event");
3201        // Roo.log(this.triggerEl.dom);
3202         
3203         this.triggerEl.on('click', this.onTriggerClick, this);
3204         
3205         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3206         
3207         
3208         if (this.triggerEl.hasClass('nav-item')) {
3209             // dropdown toggle on the 'a' in BS4?
3210             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3211         } else {
3212             this.triggerEl.addClass('dropdown-toggle');
3213         }
3214         if (Roo.isTouch) {
3215             this.el.on('touchstart'  , this.onTouch, this);
3216         }
3217         this.el.on('click' , this.onClick, this);
3218
3219         this.el.on("mouseover", this.onMouseOver, this);
3220         this.el.on("mouseout", this.onMouseOut, this);
3221         
3222     },
3223     
3224     findTargetItem : function(e)
3225     {
3226         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3227         if(!t){
3228             return false;
3229         }
3230         //Roo.log(t);         Roo.log(t.id);
3231         if(t && t.id){
3232             //Roo.log(this.menuitems);
3233             return this.menuitems.get(t.id);
3234             
3235             //return this.items.get(t.menuItemId);
3236         }
3237         
3238         return false;
3239     },
3240     
3241     onTouch : function(e) 
3242     {
3243         Roo.log("menu.onTouch");
3244         //e.stopEvent(); this make the user popdown broken
3245         this.onClick(e);
3246     },
3247     
3248     onClick : function(e)
3249     {
3250         Roo.log("menu.onClick");
3251         
3252         var t = this.findTargetItem(e);
3253         if(!t || t.isContainer){
3254             return;
3255         }
3256         Roo.log(e);
3257         /*
3258         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3259             if(t == this.activeItem && t.shouldDeactivate(e)){
3260                 this.activeItem.deactivate();
3261                 delete this.activeItem;
3262                 return;
3263             }
3264             if(t.canActivate){
3265                 this.setActiveItem(t, true);
3266             }
3267             return;
3268             
3269             
3270         }
3271         */
3272        
3273         Roo.log('pass click event');
3274         
3275         t.onClick(e);
3276         
3277         this.fireEvent("click", this, t, e);
3278         
3279         var _this = this;
3280         
3281         if(!t.href.length || t.href == '#'){
3282             (function() { _this.hide(); }).defer(100);
3283         }
3284         
3285     },
3286     
3287     onMouseOver : function(e){
3288         var t  = this.findTargetItem(e);
3289         //Roo.log(t);
3290         //if(t){
3291         //    if(t.canActivate && !t.disabled){
3292         //        this.setActiveItem(t, true);
3293         //    }
3294         //}
3295         
3296         this.fireEvent("mouseover", this, e, t);
3297     },
3298     isVisible : function(){
3299         return !this.hidden;
3300     },
3301     onMouseOut : function(e){
3302         var t  = this.findTargetItem(e);
3303         
3304         //if(t ){
3305         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3306         //        this.activeItem.deactivate();
3307         //        delete this.activeItem;
3308         //    }
3309         //}
3310         this.fireEvent("mouseout", this, e, t);
3311     },
3312     
3313     
3314     /**
3315      * Displays this menu relative to another element
3316      * @param {String/HTMLElement/Roo.Element} element The element to align to
3317      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3318      * the element (defaults to this.defaultAlign)
3319      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3320      */
3321     show : function(el, pos, parentMenu)
3322     {
3323         if (false === this.fireEvent("beforeshow", this)) {
3324             Roo.log("show canceled");
3325             return;
3326         }
3327         this.parentMenu = parentMenu;
3328         if(!this.el){
3329             this.render();
3330         }
3331         
3332         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3333     },
3334      /**
3335      * Displays this menu at a specific xy position
3336      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3337      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3338      */
3339     showAt : function(xy, parentMenu, /* private: */_e){
3340         this.parentMenu = parentMenu;
3341         if(!this.el){
3342             this.render();
3343         }
3344         if(_e !== false){
3345             this.fireEvent("beforeshow", this);
3346             //xy = this.el.adjustForConstraints(xy);
3347         }
3348         
3349         //this.el.show();
3350         this.hideMenuItems();
3351         this.hidden = false;
3352         this.triggerEl.addClass('open');
3353         this.el.addClass('show');
3354         
3355         // reassign x when hitting right
3356         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3357             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3358         }
3359         
3360         // reassign y when hitting bottom
3361         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3362             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3363         }
3364         
3365         // but the list may align on trigger left or trigger top... should it be a properity?
3366         
3367         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3368             this.el.setXY(xy);
3369         }
3370         
3371         this.focus();
3372         this.fireEvent("show", this);
3373     },
3374     
3375     focus : function(){
3376         return;
3377         if(!this.hidden){
3378             this.doFocus.defer(50, this);
3379         }
3380     },
3381
3382     doFocus : function(){
3383         if(!this.hidden){
3384             this.focusEl.focus();
3385         }
3386     },
3387
3388     /**
3389      * Hides this menu and optionally all parent menus
3390      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3391      */
3392     hide : function(deep)
3393     {
3394         if (false === this.fireEvent("beforehide", this)) {
3395             Roo.log("hide canceled");
3396             return;
3397         }
3398         this.hideMenuItems();
3399         if(this.el && this.isVisible()){
3400            
3401             if(this.activeItem){
3402                 this.activeItem.deactivate();
3403                 this.activeItem = null;
3404             }
3405             this.triggerEl.removeClass('open');;
3406             this.el.removeClass('show');
3407             this.hidden = true;
3408             this.fireEvent("hide", this);
3409         }
3410         if(deep === true && this.parentMenu){
3411             this.parentMenu.hide(true);
3412         }
3413     },
3414     
3415     onTriggerClick : function(e)
3416     {
3417         Roo.log('trigger click');
3418         
3419         var target = e.getTarget();
3420         
3421         Roo.log(target.nodeName.toLowerCase());
3422         
3423         if(target.nodeName.toLowerCase() === 'i'){
3424             e.preventDefault();
3425         }
3426         
3427     },
3428     
3429     onTriggerPress  : function(e)
3430     {
3431         Roo.log('trigger press');
3432         //Roo.log(e.getTarget());
3433        // Roo.log(this.triggerEl.dom);
3434        
3435         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3436         var pel = Roo.get(e.getTarget());
3437         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3438             Roo.log('is treeview or dropdown?');
3439             return;
3440         }
3441         
3442         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3443             return;
3444         }
3445         
3446         if (this.isVisible()) {
3447             Roo.log('hide');
3448             this.hide();
3449         } else {
3450             Roo.log('show');
3451             this.show(this.triggerEl, '?', false);
3452         }
3453         
3454         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3455             e.stopEvent();
3456         }
3457         
3458     },
3459        
3460     
3461     hideMenuItems : function()
3462     {
3463         Roo.log("hide Menu Items");
3464         if (!this.el) { 
3465             return;
3466         }
3467         
3468         this.el.select('.open',true).each(function(aa) {
3469             
3470             aa.removeClass('open');
3471          
3472         });
3473     },
3474     addxtypeChild : function (tree, cntr) {
3475         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3476           
3477         this.menuitems.add(comp);
3478         return comp;
3479
3480     },
3481     getEl : function()
3482     {
3483         Roo.log(this.el);
3484         return this.el;
3485     },
3486     
3487     clear : function()
3488     {
3489         this.getEl().dom.innerHTML = '';
3490         this.menuitems.clear();
3491     }
3492 });
3493
3494  
3495  /*
3496  * - LGPL
3497  *
3498  * menu item
3499  * 
3500  */
3501
3502
3503 /**
3504  * @class Roo.bootstrap.MenuItem
3505  * @extends Roo.bootstrap.Component
3506  * Bootstrap MenuItem class
3507  * @cfg {String} html the menu label
3508  * @cfg {String} href the link
3509  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3510  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3511  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3512  * @cfg {String} fa favicon to show on left of menu item.
3513  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3514  * 
3515  * 
3516  * @constructor
3517  * Create a new MenuItem
3518  * @param {Object} config The config object
3519  */
3520
3521
3522 Roo.bootstrap.MenuItem = function(config){
3523     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3524     this.addEvents({
3525         // raw events
3526         /**
3527          * @event click
3528          * The raw click event for the entire grid.
3529          * @param {Roo.bootstrap.MenuItem} this
3530          * @param {Roo.EventObject} e
3531          */
3532         "click" : true
3533     });
3534 };
3535
3536 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3537     
3538     href : false,
3539     html : false,
3540     preventDefault: false,
3541     isContainer : false,
3542     active : false,
3543     fa: false,
3544     
3545     getAutoCreate : function(){
3546         
3547         if(this.isContainer){
3548             return {
3549                 tag: 'li',
3550                 cls: 'dropdown-menu-item '
3551             };
3552         }
3553         var ctag = {
3554             tag: 'span',
3555             html: 'Link'
3556         };
3557         
3558         var anc = {
3559             tag : 'a',
3560             cls : 'dropdown-item',
3561             href : '#',
3562             cn : [  ]
3563         };
3564         
3565         if (this.fa !== false) {
3566             anc.cn.push({
3567                 tag : 'i',
3568                 cls : 'fa fa-' + this.fa
3569             });
3570         }
3571         
3572         anc.cn.push(ctag);
3573         
3574         
3575         var cfg= {
3576             tag: 'li',
3577             cls: 'dropdown-menu-item',
3578             cn: [ anc ]
3579         };
3580         if (this.parent().type == 'treeview') {
3581             cfg.cls = 'treeview-menu';
3582         }
3583         if (this.active) {
3584             cfg.cls += ' active';
3585         }
3586         
3587         
3588         
3589         anc.href = this.href || cfg.cn[0].href ;
3590         ctag.html = this.html || cfg.cn[0].html ;
3591         return cfg;
3592     },
3593     
3594     initEvents: function()
3595     {
3596         if (this.parent().type == 'treeview') {
3597             this.el.select('a').on('click', this.onClick, this);
3598         }
3599         
3600         if (this.menu) {
3601             this.menu.parentType = this.xtype;
3602             this.menu.triggerEl = this.el;
3603             this.menu = this.addxtype(Roo.apply({}, this.menu));
3604         }
3605         
3606     },
3607     onClick : function(e)
3608     {
3609         Roo.log('item on click ');
3610         
3611         if(this.preventDefault){
3612             e.preventDefault();
3613         }
3614         //this.parent().hideMenuItems();
3615         
3616         this.fireEvent('click', this, e);
3617     },
3618     getEl : function()
3619     {
3620         return this.el;
3621     } 
3622 });
3623
3624  
3625
3626  /*
3627  * - LGPL
3628  *
3629  * menu separator
3630  * 
3631  */
3632
3633
3634 /**
3635  * @class Roo.bootstrap.MenuSeparator
3636  * @extends Roo.bootstrap.Component
3637  * Bootstrap MenuSeparator class
3638  * 
3639  * @constructor
3640  * Create a new MenuItem
3641  * @param {Object} config The config object
3642  */
3643
3644
3645 Roo.bootstrap.MenuSeparator = function(config){
3646     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3647 };
3648
3649 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3650     
3651     getAutoCreate : function(){
3652         var cfg = {
3653             cls: 'divider',
3654             tag : 'li'
3655         };
3656         
3657         return cfg;
3658     }
3659    
3660 });
3661
3662  
3663
3664  
3665 /*
3666 * Licence: LGPL
3667 */
3668
3669 /**
3670  * @class Roo.bootstrap.Modal
3671  * @extends Roo.bootstrap.Component
3672  * Bootstrap Modal class
3673  * @cfg {String} title Title of dialog
3674  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3675  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3676  * @cfg {Boolean} specificTitle default false
3677  * @cfg {Array} buttons Array of buttons or standard button set..
3678  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3679  * @cfg {Boolean} animate default true
3680  * @cfg {Boolean} allow_close default true
3681  * @cfg {Boolean} fitwindow default false
3682  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3683  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3684  * @cfg {String} size (sm|lg) default empty
3685  * @cfg {Number} max_width set the max width of modal
3686  *
3687  *
3688  * @constructor
3689  * Create a new Modal Dialog
3690  * @param {Object} config The config object
3691  */
3692
3693 Roo.bootstrap.Modal = function(config){
3694     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3695     this.addEvents({
3696         // raw events
3697         /**
3698          * @event btnclick
3699          * The raw btnclick event for the button
3700          * @param {Roo.EventObject} e
3701          */
3702         "btnclick" : true,
3703         /**
3704          * @event resize
3705          * Fire when dialog resize
3706          * @param {Roo.bootstrap.Modal} this
3707          * @param {Roo.EventObject} e
3708          */
3709         "resize" : true
3710     });
3711     this.buttons = this.buttons || [];
3712
3713     if (this.tmpl) {
3714         this.tmpl = Roo.factory(this.tmpl);
3715     }
3716
3717 };
3718
3719 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3720
3721     title : 'test dialog',
3722
3723     buttons : false,
3724
3725     // set on load...
3726
3727     html: false,
3728
3729     tmp: false,
3730
3731     specificTitle: false,
3732
3733     buttonPosition: 'right',
3734
3735     allow_close : true,
3736
3737     animate : true,
3738
3739     fitwindow: false,
3740     
3741      // private
3742     dialogEl: false,
3743     bodyEl:  false,
3744     footerEl:  false,
3745     titleEl:  false,
3746     closeEl:  false,
3747
3748     size: '',
3749     
3750     max_width: 0,
3751     
3752     max_height: 0,
3753     
3754     fit_content: false,
3755
3756     onRender : function(ct, position)
3757     {
3758         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3759
3760         if(!this.el){
3761             var cfg = Roo.apply({},  this.getAutoCreate());
3762             cfg.id = Roo.id();
3763             //if(!cfg.name){
3764             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3765             //}
3766             //if (!cfg.name.length) {
3767             //    delete cfg.name;
3768            // }
3769             if (this.cls) {
3770                 cfg.cls += ' ' + this.cls;
3771             }
3772             if (this.style) {
3773                 cfg.style = this.style;
3774             }
3775             this.el = Roo.get(document.body).createChild(cfg, position);
3776         }
3777         //var type = this.el.dom.type;
3778
3779
3780         if(this.tabIndex !== undefined){
3781             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3782         }
3783
3784         this.dialogEl = this.el.select('.modal-dialog',true).first();
3785         this.bodyEl = this.el.select('.modal-body',true).first();
3786         this.closeEl = this.el.select('.modal-header .close', true).first();
3787         this.headerEl = this.el.select('.modal-header',true).first();
3788         this.titleEl = this.el.select('.modal-title',true).first();
3789         this.footerEl = this.el.select('.modal-footer',true).first();
3790
3791         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3792         
3793         //this.el.addClass("x-dlg-modal");
3794
3795         if (this.buttons.length) {
3796             Roo.each(this.buttons, function(bb) {
3797                 var b = Roo.apply({}, bb);
3798                 b.xns = b.xns || Roo.bootstrap;
3799                 b.xtype = b.xtype || 'Button';
3800                 if (typeof(b.listeners) == 'undefined') {
3801                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3802                 }
3803
3804                 var btn = Roo.factory(b);
3805
3806                 btn.render(this.getButtonContainer());
3807
3808             },this);
3809         }
3810         // render the children.
3811         var nitems = [];
3812
3813         if(typeof(this.items) != 'undefined'){
3814             var items = this.items;
3815             delete this.items;
3816
3817             for(var i =0;i < items.length;i++) {
3818                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3819             }
3820         }
3821
3822         this.items = nitems;
3823
3824         // where are these used - they used to be body/close/footer
3825
3826
3827         this.initEvents();
3828         //this.el.addClass([this.fieldClass, this.cls]);
3829
3830     },
3831
3832     getAutoCreate : function()
3833     {
3834         // we will default to modal-body-overflow - might need to remove or make optional later.
3835         var bdy = {
3836                 cls : 'modal-body enable-modal-body-overflow ', 
3837                 html : this.html || ''
3838         };
3839
3840         var title = {
3841             tag: 'h4',
3842             cls : 'modal-title',
3843             html : this.title
3844         };
3845
3846         if(this.specificTitle){
3847             title = this.title;
3848
3849         }
3850
3851         var header = [];
3852         if (this.allow_close && Roo.bootstrap.version == 3) {
3853             header.push({
3854                 tag: 'button',
3855                 cls : 'close',
3856                 html : '&times'
3857             });
3858         }
3859
3860         header.push(title);
3861
3862         if (this.allow_close && Roo.bootstrap.version == 4) {
3863             header.push({
3864                 tag: 'button',
3865                 cls : 'close',
3866                 html : '&times'
3867             });
3868         }
3869         
3870         var size = '';
3871
3872         if(this.size.length){
3873             size = 'modal-' + this.size;
3874         }
3875         
3876         var footer = Roo.bootstrap.version == 3 ?
3877             {
3878                 cls : 'modal-footer',
3879                 cn : [
3880                     {
3881                         tag: 'div',
3882                         cls: 'btn-' + this.buttonPosition
3883                     }
3884                 ]
3885
3886             } :
3887             {  // BS4 uses mr-auto on left buttons....
3888                 cls : 'modal-footer'
3889             };
3890
3891             
3892
3893         
3894         
3895         var modal = {
3896             cls: "modal",
3897              cn : [
3898                 {
3899                     cls: "modal-dialog " + size,
3900                     cn : [
3901                         {
3902                             cls : "modal-content",
3903                             cn : [
3904                                 {
3905                                     cls : 'modal-header',
3906                                     cn : header
3907                                 },
3908                                 bdy,
3909                                 footer
3910                             ]
3911
3912                         }
3913                     ]
3914
3915                 }
3916             ]
3917         };
3918
3919         if(this.animate){
3920             modal.cls += ' fade';
3921         }
3922
3923         return modal;
3924
3925     },
3926     getChildContainer : function() {
3927
3928          return this.bodyEl;
3929
3930     },
3931     getButtonContainer : function() {
3932         
3933          return Roo.bootstrap.version == 4 ?
3934             this.el.select('.modal-footer',true).first()
3935             : this.el.select('.modal-footer div',true).first();
3936
3937     },
3938     initEvents : function()
3939     {
3940         if (this.allow_close) {
3941             this.closeEl.on('click', this.hide, this);
3942         }
3943         Roo.EventManager.onWindowResize(this.resize, this, true);
3944
3945
3946     },
3947   
3948
3949     resize : function()
3950     {
3951         this.maskEl.setSize(
3952             Roo.lib.Dom.getViewWidth(true),
3953             Roo.lib.Dom.getViewHeight(true)
3954         );
3955         
3956         if (this.fitwindow) {
3957             
3958            
3959             this.setSize(
3960                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3961                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3962             );
3963             return;
3964         }
3965         
3966         if(this.max_width !== 0) {
3967             
3968             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3969             
3970             if(this.height) {
3971                 this.setSize(w, this.height);
3972                 return;
3973             }
3974             
3975             if(this.max_height) {
3976                 this.setSize(w,Math.min(
3977                     this.max_height,
3978                     Roo.lib.Dom.getViewportHeight(true) - 60
3979                 ));
3980                 
3981                 return;
3982             }
3983             
3984             if(!this.fit_content) {
3985                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3986                 return;
3987             }
3988             
3989             this.setSize(w, Math.min(
3990                 60 +
3991                 this.headerEl.getHeight() + 
3992                 this.footerEl.getHeight() + 
3993                 this.getChildHeight(this.bodyEl.dom.childNodes),
3994                 Roo.lib.Dom.getViewportHeight(true) - 60)
3995             );
3996         }
3997         
3998     },
3999
4000     setSize : function(w,h)
4001     {
4002         if (!w && !h) {
4003             return;
4004         }
4005         
4006         this.resizeTo(w,h);
4007     },
4008
4009     show : function() {
4010
4011         if (!this.rendered) {
4012             this.render();
4013         }
4014
4015         //this.el.setStyle('display', 'block');
4016         this.el.removeClass('hideing');
4017         this.el.dom.style.display='block';
4018         
4019         Roo.get(document.body).addClass('modal-open');
4020  
4021         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4022             
4023             (function(){
4024                 this.el.addClass('show');
4025                 this.el.addClass('in');
4026             }).defer(50, this);
4027         }else{
4028             this.el.addClass('show');
4029             this.el.addClass('in');
4030         }
4031
4032         // not sure how we can show data in here..
4033         //if (this.tmpl) {
4034         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4035         //}
4036
4037         Roo.get(document.body).addClass("x-body-masked");
4038         
4039         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4040         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4041         this.maskEl.dom.style.display = 'block';
4042         this.maskEl.addClass('show');
4043         
4044         
4045         this.resize();
4046         
4047         this.fireEvent('show', this);
4048
4049         // set zindex here - otherwise it appears to be ignored...
4050         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4051
4052         (function () {
4053             this.items.forEach( function(e) {
4054                 e.layout ? e.layout() : false;
4055
4056             });
4057         }).defer(100,this);
4058
4059     },
4060     hide : function()
4061     {
4062         if(this.fireEvent("beforehide", this) !== false){
4063             
4064             this.maskEl.removeClass('show');
4065             
4066             this.maskEl.dom.style.display = '';
4067             Roo.get(document.body).removeClass("x-body-masked");
4068             this.el.removeClass('in');
4069             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4070
4071             if(this.animate){ // why
4072                 this.el.addClass('hideing');
4073                 this.el.removeClass('show');
4074                 (function(){
4075                     if (!this.el.hasClass('hideing')) {
4076                         return; // it's been shown again...
4077                     }
4078                     
4079                     this.el.dom.style.display='';
4080
4081                     Roo.get(document.body).removeClass('modal-open');
4082                     this.el.removeClass('hideing');
4083                 }).defer(150,this);
4084                 
4085             }else{
4086                 this.el.removeClass('show');
4087                 this.el.dom.style.display='';
4088                 Roo.get(document.body).removeClass('modal-open');
4089
4090             }
4091             this.fireEvent('hide', this);
4092         }
4093     },
4094     isVisible : function()
4095     {
4096         
4097         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4098         
4099     },
4100
4101     addButton : function(str, cb)
4102     {
4103
4104
4105         var b = Roo.apply({}, { html : str } );
4106         b.xns = b.xns || Roo.bootstrap;
4107         b.xtype = b.xtype || 'Button';
4108         if (typeof(b.listeners) == 'undefined') {
4109             b.listeners = { click : cb.createDelegate(this)  };
4110         }
4111
4112         var btn = Roo.factory(b);
4113
4114         btn.render(this.getButtonContainer());
4115
4116         return btn;
4117
4118     },
4119
4120     setDefaultButton : function(btn)
4121     {
4122         //this.el.select('.modal-footer').()
4123     },
4124
4125     resizeTo: function(w,h)
4126     {
4127         this.dialogEl.setWidth(w);
4128         
4129         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4130
4131         this.bodyEl.setHeight(h - diff);
4132         
4133         this.fireEvent('resize', this);
4134     },
4135     
4136     setContentSize  : function(w, h)
4137     {
4138
4139     },
4140     onButtonClick: function(btn,e)
4141     {
4142         //Roo.log([a,b,c]);
4143         this.fireEvent('btnclick', btn.name, e);
4144     },
4145      /**
4146      * Set the title of the Dialog
4147      * @param {String} str new Title
4148      */
4149     setTitle: function(str) {
4150         this.titleEl.dom.innerHTML = str;
4151     },
4152     /**
4153      * Set the body of the Dialog
4154      * @param {String} str new Title
4155      */
4156     setBody: function(str) {
4157         this.bodyEl.dom.innerHTML = str;
4158     },
4159     /**
4160      * Set the body of the Dialog using the template
4161      * @param {Obj} data - apply this data to the template and replace the body contents.
4162      */
4163     applyBody: function(obj)
4164     {
4165         if (!this.tmpl) {
4166             Roo.log("Error - using apply Body without a template");
4167             //code
4168         }
4169         this.tmpl.overwrite(this.bodyEl, obj);
4170     },
4171     
4172     getChildHeight : function(child_nodes)
4173     {
4174         if(
4175             !child_nodes ||
4176             child_nodes.length == 0
4177         ) {
4178             return;
4179         }
4180         
4181         var child_height = 0;
4182         
4183         for(var i = 0; i < child_nodes.length; i++) {
4184             
4185             /*
4186             * for modal with tabs...
4187             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4188                 
4189                 var layout_childs = child_nodes[i].childNodes;
4190                 
4191                 for(var j = 0; j < layout_childs.length; j++) {
4192                     
4193                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4194                         
4195                         var layout_body_childs = layout_childs[j].childNodes;
4196                         
4197                         for(var k = 0; k < layout_body_childs.length; k++) {
4198                             
4199                             if(layout_body_childs[k].classList.contains('navbar')) {
4200                                 child_height += layout_body_childs[k].offsetHeight;
4201                                 continue;
4202                             }
4203                             
4204                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4205                                 
4206                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4207                                 
4208                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4209                                     
4210                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4211                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4212                                         continue;
4213                                     }
4214                                     
4215                                 }
4216                                 
4217                             }
4218                             
4219                         }
4220                     }
4221                 }
4222                 continue;
4223             }
4224             */
4225             
4226             child_height += child_nodes[i].offsetHeight;
4227             // Roo.log(child_nodes[i].offsetHeight);
4228         }
4229         
4230         return child_height;
4231     }
4232
4233 });
4234
4235
4236 Roo.apply(Roo.bootstrap.Modal,  {
4237     /**
4238          * Button config that displays a single OK button
4239          * @type Object
4240          */
4241         OK :  [{
4242             name : 'ok',
4243             weight : 'primary',
4244             html : 'OK'
4245         }],
4246         /**
4247          * Button config that displays Yes and No buttons
4248          * @type Object
4249          */
4250         YESNO : [
4251             {
4252                 name  : 'no',
4253                 html : 'No'
4254             },
4255             {
4256                 name  :'yes',
4257                 weight : 'primary',
4258                 html : 'Yes'
4259             }
4260         ],
4261
4262         /**
4263          * Button config that displays OK and Cancel buttons
4264          * @type Object
4265          */
4266         OKCANCEL : [
4267             {
4268                name : 'cancel',
4269                 html : 'Cancel'
4270             },
4271             {
4272                 name : 'ok',
4273                 weight : 'primary',
4274                 html : 'OK'
4275             }
4276         ],
4277         /**
4278          * Button config that displays Yes, No and Cancel buttons
4279          * @type Object
4280          */
4281         YESNOCANCEL : [
4282             {
4283                 name : 'yes',
4284                 weight : 'primary',
4285                 html : 'Yes'
4286             },
4287             {
4288                 name : 'no',
4289                 html : 'No'
4290             },
4291             {
4292                 name : 'cancel',
4293                 html : 'Cancel'
4294             }
4295         ],
4296         
4297         zIndex : 10001
4298 });
4299 /*
4300  * - LGPL
4301  *
4302  * messagebox - can be used as a replace
4303  * 
4304  */
4305 /**
4306  * @class Roo.MessageBox
4307  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4308  * Example usage:
4309  *<pre><code>
4310 // Basic alert:
4311 Roo.Msg.alert('Status', 'Changes saved successfully.');
4312
4313 // Prompt for user data:
4314 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4315     if (btn == 'ok'){
4316         // process text value...
4317     }
4318 });
4319
4320 // Show a dialog using config options:
4321 Roo.Msg.show({
4322    title:'Save Changes?',
4323    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4324    buttons: Roo.Msg.YESNOCANCEL,
4325    fn: processResult,
4326    animEl: 'elId'
4327 });
4328 </code></pre>
4329  * @singleton
4330  */
4331 Roo.bootstrap.MessageBox = function(){
4332     var dlg, opt, mask, waitTimer;
4333     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4334     var buttons, activeTextEl, bwidth;
4335
4336     
4337     // private
4338     var handleButton = function(button){
4339         dlg.hide();
4340         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4341     };
4342
4343     // private
4344     var handleHide = function(){
4345         if(opt && opt.cls){
4346             dlg.el.removeClass(opt.cls);
4347         }
4348         //if(waitTimer){
4349         //    Roo.TaskMgr.stop(waitTimer);
4350         //    waitTimer = null;
4351         //}
4352     };
4353
4354     // private
4355     var updateButtons = function(b){
4356         var width = 0;
4357         if(!b){
4358             buttons["ok"].hide();
4359             buttons["cancel"].hide();
4360             buttons["yes"].hide();
4361             buttons["no"].hide();
4362             dlg.footerEl.hide();
4363             
4364             return width;
4365         }
4366         dlg.footerEl.show();
4367         for(var k in buttons){
4368             if(typeof buttons[k] != "function"){
4369                 if(b[k]){
4370                     buttons[k].show();
4371                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4372                     width += buttons[k].el.getWidth()+15;
4373                 }else{
4374                     buttons[k].hide();
4375                 }
4376             }
4377         }
4378         return width;
4379     };
4380
4381     // private
4382     var handleEsc = function(d, k, e){
4383         if(opt && opt.closable !== false){
4384             dlg.hide();
4385         }
4386         if(e){
4387             e.stopEvent();
4388         }
4389     };
4390
4391     return {
4392         /**
4393          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4394          * @return {Roo.BasicDialog} The BasicDialog element
4395          */
4396         getDialog : function(){
4397            if(!dlg){
4398                 dlg = new Roo.bootstrap.Modal( {
4399                     //draggable: true,
4400                     //resizable:false,
4401                     //constraintoviewport:false,
4402                     //fixedcenter:true,
4403                     //collapsible : false,
4404                     //shim:true,
4405                     //modal: true,
4406                 //    width: 'auto',
4407                   //  height:100,
4408                     //buttonAlign:"center",
4409                     closeClick : function(){
4410                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4411                             handleButton("no");
4412                         }else{
4413                             handleButton("cancel");
4414                         }
4415                     }
4416                 });
4417                 dlg.render();
4418                 dlg.on("hide", handleHide);
4419                 mask = dlg.mask;
4420                 //dlg.addKeyListener(27, handleEsc);
4421                 buttons = {};
4422                 this.buttons = buttons;
4423                 var bt = this.buttonText;
4424                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4425                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4426                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4427                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4428                 //Roo.log(buttons);
4429                 bodyEl = dlg.bodyEl.createChild({
4430
4431                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4432                         '<textarea class="roo-mb-textarea"></textarea>' +
4433                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4434                 });
4435                 msgEl = bodyEl.dom.firstChild;
4436                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4437                 textboxEl.enableDisplayMode();
4438                 textboxEl.addKeyListener([10,13], function(){
4439                     if(dlg.isVisible() && opt && opt.buttons){
4440                         if(opt.buttons.ok){
4441                             handleButton("ok");
4442                         }else if(opt.buttons.yes){
4443                             handleButton("yes");
4444                         }
4445                     }
4446                 });
4447                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4448                 textareaEl.enableDisplayMode();
4449                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4450                 progressEl.enableDisplayMode();
4451                 
4452                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4453                 var pf = progressEl.dom.firstChild;
4454                 if (pf) {
4455                     pp = Roo.get(pf.firstChild);
4456                     pp.setHeight(pf.offsetHeight);
4457                 }
4458                 
4459             }
4460             return dlg;
4461         },
4462
4463         /**
4464          * Updates the message box body text
4465          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4466          * the XHTML-compliant non-breaking space character '&amp;#160;')
4467          * @return {Roo.MessageBox} This message box
4468          */
4469         updateText : function(text)
4470         {
4471             if(!dlg.isVisible() && !opt.width){
4472                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4473                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4474             }
4475             msgEl.innerHTML = text || '&#160;';
4476       
4477             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4478             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4479             var w = Math.max(
4480                     Math.min(opt.width || cw , this.maxWidth), 
4481                     Math.max(opt.minWidth || this.minWidth, bwidth)
4482             );
4483             if(opt.prompt){
4484                 activeTextEl.setWidth(w);
4485             }
4486             if(dlg.isVisible()){
4487                 dlg.fixedcenter = false;
4488             }
4489             // to big, make it scroll. = But as usual stupid IE does not support
4490             // !important..
4491             
4492             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4493                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4494                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4495             } else {
4496                 bodyEl.dom.style.height = '';
4497                 bodyEl.dom.style.overflowY = '';
4498             }
4499             if (cw > w) {
4500                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4501             } else {
4502                 bodyEl.dom.style.overflowX = '';
4503             }
4504             
4505             dlg.setContentSize(w, bodyEl.getHeight());
4506             if(dlg.isVisible()){
4507                 dlg.fixedcenter = true;
4508             }
4509             return this;
4510         },
4511
4512         /**
4513          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4514          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4515          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4516          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4517          * @return {Roo.MessageBox} This message box
4518          */
4519         updateProgress : function(value, text){
4520             if(text){
4521                 this.updateText(text);
4522             }
4523             
4524             if (pp) { // weird bug on my firefox - for some reason this is not defined
4525                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4526                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4527             }
4528             return this;
4529         },        
4530
4531         /**
4532          * Returns true if the message box is currently displayed
4533          * @return {Boolean} True if the message box is visible, else false
4534          */
4535         isVisible : function(){
4536             return dlg && dlg.isVisible();  
4537         },
4538
4539         /**
4540          * Hides the message box if it is displayed
4541          */
4542         hide : function(){
4543             if(this.isVisible()){
4544                 dlg.hide();
4545             }  
4546         },
4547
4548         /**
4549          * Displays a new message box, or reinitializes an existing message box, based on the config options
4550          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4551          * The following config object properties are supported:
4552          * <pre>
4553 Property    Type             Description
4554 ----------  ---------------  ------------------------------------------------------------------------------------
4555 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4556                                    closes (defaults to undefined)
4557 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4558                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4559 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4560                                    progress and wait dialogs will ignore this property and always hide the
4561                                    close button as they can only be closed programmatically.
4562 cls               String           A custom CSS class to apply to the message box element
4563 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4564                                    displayed (defaults to 75)
4565 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4566                                    function will be btn (the name of the button that was clicked, if applicable,
4567                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4568                                    Progress and wait dialogs will ignore this option since they do not respond to
4569                                    user actions and can only be closed programmatically, so any required function
4570                                    should be called by the same code after it closes the dialog.
4571 icon              String           A CSS class that provides a background image to be used as an icon for
4572                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4573 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4574 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4575 modal             Boolean          False to allow user interaction with the page while the message box is
4576                                    displayed (defaults to true)
4577 msg               String           A string that will replace the existing message box body text (defaults
4578                                    to the XHTML-compliant non-breaking space character '&#160;')
4579 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4580 progress          Boolean          True to display a progress bar (defaults to false)
4581 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4582 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4583 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4584 title             String           The title text
4585 value             String           The string value to set into the active textbox element if displayed
4586 wait              Boolean          True to display a progress bar (defaults to false)
4587 width             Number           The width of the dialog in pixels
4588 </pre>
4589          *
4590          * Example usage:
4591          * <pre><code>
4592 Roo.Msg.show({
4593    title: 'Address',
4594    msg: 'Please enter your address:',
4595    width: 300,
4596    buttons: Roo.MessageBox.OKCANCEL,
4597    multiline: true,
4598    fn: saveAddress,
4599    animEl: 'addAddressBtn'
4600 });
4601 </code></pre>
4602          * @param {Object} config Configuration options
4603          * @return {Roo.MessageBox} This message box
4604          */
4605         show : function(options)
4606         {
4607             
4608             // this causes nightmares if you show one dialog after another
4609             // especially on callbacks..
4610              
4611             if(this.isVisible()){
4612                 
4613                 this.hide();
4614                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4615                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4616                 Roo.log("New Dialog Message:" +  options.msg )
4617                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4618                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4619                 
4620             }
4621             var d = this.getDialog();
4622             opt = options;
4623             d.setTitle(opt.title || "&#160;");
4624             d.closeEl.setDisplayed(opt.closable !== false);
4625             activeTextEl = textboxEl;
4626             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4627             if(opt.prompt){
4628                 if(opt.multiline){
4629                     textboxEl.hide();
4630                     textareaEl.show();
4631                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4632                         opt.multiline : this.defaultTextHeight);
4633                     activeTextEl = textareaEl;
4634                 }else{
4635                     textboxEl.show();
4636                     textareaEl.hide();
4637                 }
4638             }else{
4639                 textboxEl.hide();
4640                 textareaEl.hide();
4641             }
4642             progressEl.setDisplayed(opt.progress === true);
4643             if (opt.progress) {
4644                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4645             }
4646             this.updateProgress(0);
4647             activeTextEl.dom.value = opt.value || "";
4648             if(opt.prompt){
4649                 dlg.setDefaultButton(activeTextEl);
4650             }else{
4651                 var bs = opt.buttons;
4652                 var db = null;
4653                 if(bs && bs.ok){
4654                     db = buttons["ok"];
4655                 }else if(bs && bs.yes){
4656                     db = buttons["yes"];
4657                 }
4658                 dlg.setDefaultButton(db);
4659             }
4660             bwidth = updateButtons(opt.buttons);
4661             this.updateText(opt.msg);
4662             if(opt.cls){
4663                 d.el.addClass(opt.cls);
4664             }
4665             d.proxyDrag = opt.proxyDrag === true;
4666             d.modal = opt.modal !== false;
4667             d.mask = opt.modal !== false ? mask : false;
4668             if(!d.isVisible()){
4669                 // force it to the end of the z-index stack so it gets a cursor in FF
4670                 document.body.appendChild(dlg.el.dom);
4671                 d.animateTarget = null;
4672                 d.show(options.animEl);
4673             }
4674             return this;
4675         },
4676
4677         /**
4678          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4679          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4680          * and closing the message box when the process is complete.
4681          * @param {String} title The title bar text
4682          * @param {String} msg The message box body text
4683          * @return {Roo.MessageBox} This message box
4684          */
4685         progress : function(title, msg){
4686             this.show({
4687                 title : title,
4688                 msg : msg,
4689                 buttons: false,
4690                 progress:true,
4691                 closable:false,
4692                 minWidth: this.minProgressWidth,
4693                 modal : true
4694             });
4695             return this;
4696         },
4697
4698         /**
4699          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4700          * If a callback function is passed it will be called after the user clicks the button, and the
4701          * id of the button that was clicked will be passed as the only parameter to the callback
4702          * (could also be the top-right close button).
4703          * @param {String} title The title bar text
4704          * @param {String} msg The message box body text
4705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4706          * @param {Object} scope (optional) The scope of the callback function
4707          * @return {Roo.MessageBox} This message box
4708          */
4709         alert : function(title, msg, fn, scope)
4710         {
4711             this.show({
4712                 title : title,
4713                 msg : msg,
4714                 buttons: this.OK,
4715                 fn: fn,
4716                 closable : false,
4717                 scope : scope,
4718                 modal : true
4719             });
4720             return this;
4721         },
4722
4723         /**
4724          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4725          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4726          * You are responsible for closing the message box when the process is complete.
4727          * @param {String} msg The message box body text
4728          * @param {String} title (optional) The title bar text
4729          * @return {Roo.MessageBox} This message box
4730          */
4731         wait : function(msg, title){
4732             this.show({
4733                 title : title,
4734                 msg : msg,
4735                 buttons: false,
4736                 closable:false,
4737                 progress:true,
4738                 modal:true,
4739                 width:300,
4740                 wait:true
4741             });
4742             waitTimer = Roo.TaskMgr.start({
4743                 run: function(i){
4744                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4745                 },
4746                 interval: 1000
4747             });
4748             return this;
4749         },
4750
4751         /**
4752          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4753          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4754          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4755          * @param {String} title The title bar text
4756          * @param {String} msg The message box body text
4757          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4758          * @param {Object} scope (optional) The scope of the callback function
4759          * @return {Roo.MessageBox} This message box
4760          */
4761         confirm : function(title, msg, fn, scope){
4762             this.show({
4763                 title : title,
4764                 msg : msg,
4765                 buttons: this.YESNO,
4766                 fn: fn,
4767                 scope : scope,
4768                 modal : true
4769             });
4770             return this;
4771         },
4772
4773         /**
4774          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4775          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4776          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4777          * (could also be the top-right close button) and the text that was entered will be passed as the two
4778          * parameters to the callback.
4779          * @param {String} title The title bar text
4780          * @param {String} msg The message box body text
4781          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4782          * @param {Object} scope (optional) The scope of the callback function
4783          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4784          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4785          * @return {Roo.MessageBox} This message box
4786          */
4787         prompt : function(title, msg, fn, scope, multiline){
4788             this.show({
4789                 title : title,
4790                 msg : msg,
4791                 buttons: this.OKCANCEL,
4792                 fn: fn,
4793                 minWidth:250,
4794                 scope : scope,
4795                 prompt:true,
4796                 multiline: multiline,
4797                 modal : true
4798             });
4799             return this;
4800         },
4801
4802         /**
4803          * Button config that displays a single OK button
4804          * @type Object
4805          */
4806         OK : {ok:true},
4807         /**
4808          * Button config that displays Yes and No buttons
4809          * @type Object
4810          */
4811         YESNO : {yes:true, no:true},
4812         /**
4813          * Button config that displays OK and Cancel buttons
4814          * @type Object
4815          */
4816         OKCANCEL : {ok:true, cancel:true},
4817         /**
4818          * Button config that displays Yes, No and Cancel buttons
4819          * @type Object
4820          */
4821         YESNOCANCEL : {yes:true, no:true, cancel:true},
4822
4823         /**
4824          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4825          * @type Number
4826          */
4827         defaultTextHeight : 75,
4828         /**
4829          * The maximum width in pixels of the message box (defaults to 600)
4830          * @type Number
4831          */
4832         maxWidth : 600,
4833         /**
4834          * The minimum width in pixels of the message box (defaults to 100)
4835          * @type Number
4836          */
4837         minWidth : 100,
4838         /**
4839          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4840          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4841          * @type Number
4842          */
4843         minProgressWidth : 250,
4844         /**
4845          * An object containing the default button text strings that can be overriden for localized language support.
4846          * Supported properties are: ok, cancel, yes and no.
4847          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4848          * @type Object
4849          */
4850         buttonText : {
4851             ok : "OK",
4852             cancel : "Cancel",
4853             yes : "Yes",
4854             no : "No"
4855         }
4856     };
4857 }();
4858
4859 /**
4860  * Shorthand for {@link Roo.MessageBox}
4861  */
4862 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4863 Roo.Msg = Roo.Msg || Roo.MessageBox;
4864 /*
4865  * - LGPL
4866  *
4867  * navbar
4868  * 
4869  */
4870
4871 /**
4872  * @class Roo.bootstrap.Navbar
4873  * @extends Roo.bootstrap.Component
4874  * Bootstrap Navbar class
4875
4876  * @constructor
4877  * Create a new Navbar
4878  * @param {Object} config The config object
4879  */
4880
4881
4882 Roo.bootstrap.Navbar = function(config){
4883     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4884     this.addEvents({
4885         // raw events
4886         /**
4887          * @event beforetoggle
4888          * Fire before toggle the menu
4889          * @param {Roo.EventObject} e
4890          */
4891         "beforetoggle" : true
4892     });
4893 };
4894
4895 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4896     
4897     
4898    
4899     // private
4900     navItems : false,
4901     loadMask : false,
4902     
4903     
4904     getAutoCreate : function(){
4905         
4906         
4907         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4908         
4909     },
4910     
4911     initEvents :function ()
4912     {
4913         //Roo.log(this.el.select('.navbar-toggle',true));
4914         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4915         
4916         var mark = {
4917             tag: "div",
4918             cls:"x-dlg-mask"
4919         };
4920         
4921         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4922         
4923         var size = this.el.getSize();
4924         this.maskEl.setSize(size.width, size.height);
4925         this.maskEl.enableDisplayMode("block");
4926         this.maskEl.hide();
4927         
4928         if(this.loadMask){
4929             this.maskEl.show();
4930         }
4931     },
4932     
4933     
4934     getChildContainer : function()
4935     {
4936         if (this.el && this.el.select('.collapse').getCount()) {
4937             return this.el.select('.collapse',true).first();
4938         }
4939         
4940         return this.el;
4941     },
4942     
4943     mask : function()
4944     {
4945         this.maskEl.show();
4946     },
4947     
4948     unmask : function()
4949     {
4950         this.maskEl.hide();
4951     },
4952     onToggle : function()
4953     {
4954         
4955         if(this.fireEvent('beforetoggle', this) === false){
4956             return;
4957         }
4958         var ce = this.el.select('.navbar-collapse',true).first();
4959       
4960         if (!ce.hasClass('show')) {
4961            this.expand();
4962         } else {
4963             this.collapse();
4964         }
4965         
4966         
4967     
4968     },
4969     /**
4970      * Expand the navbar pulldown 
4971      */
4972     expand : function ()
4973     {
4974        
4975         var ce = this.el.select('.navbar-collapse',true).first();
4976         if (ce.hasClass('collapsing')) {
4977             return;
4978         }
4979         ce.dom.style.height = '';
4980                // show it...
4981         ce.addClass('in'); // old...
4982         ce.removeClass('collapse');
4983         ce.addClass('show');
4984         var h = ce.getHeight();
4985         Roo.log(h);
4986         ce.removeClass('show');
4987         // at this point we should be able to see it..
4988         ce.addClass('collapsing');
4989         
4990         ce.setHeight(0); // resize it ...
4991         ce.on('transitionend', function() {
4992             //Roo.log('done transition');
4993             ce.removeClass('collapsing');
4994             ce.addClass('show');
4995             ce.removeClass('collapse');
4996
4997             ce.dom.style.height = '';
4998         }, this, { single: true} );
4999         ce.setHeight(h);
5000         ce.dom.scrollTop = 0;
5001     },
5002     /**
5003      * Collapse the navbar pulldown 
5004      */
5005     collapse : function()
5006     {
5007          var ce = this.el.select('.navbar-collapse',true).first();
5008        
5009         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5010             // it's collapsed or collapsing..
5011             return;
5012         }
5013         ce.removeClass('in'); // old...
5014         ce.setHeight(ce.getHeight());
5015         ce.removeClass('show');
5016         ce.addClass('collapsing');
5017         
5018         ce.on('transitionend', function() {
5019             ce.dom.style.height = '';
5020             ce.removeClass('collapsing');
5021             ce.addClass('collapse');
5022         }, this, { single: true} );
5023         ce.setHeight(0);
5024     }
5025     
5026     
5027     
5028 });
5029
5030
5031
5032  
5033
5034  /*
5035  * - LGPL
5036  *
5037  * navbar
5038  * 
5039  */
5040
5041 /**
5042  * @class Roo.bootstrap.NavSimplebar
5043  * @extends Roo.bootstrap.Navbar
5044  * Bootstrap Sidebar class
5045  *
5046  * @cfg {Boolean} inverse is inverted color
5047  * 
5048  * @cfg {String} type (nav | pills | tabs)
5049  * @cfg {Boolean} arrangement stacked | justified
5050  * @cfg {String} align (left | right) alignment
5051  * 
5052  * @cfg {Boolean} main (true|false) main nav bar? default false
5053  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5054  * 
5055  * @cfg {String} tag (header|footer|nav|div) default is nav 
5056
5057  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5058  * 
5059  * 
5060  * @constructor
5061  * Create a new Sidebar
5062  * @param {Object} config The config object
5063  */
5064
5065
5066 Roo.bootstrap.NavSimplebar = function(config){
5067     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5071     
5072     inverse: false,
5073     
5074     type: false,
5075     arrangement: '',
5076     align : false,
5077     
5078     weight : 'light',
5079     
5080     main : false,
5081     
5082     
5083     tag : false,
5084     
5085     
5086     getAutoCreate : function(){
5087         
5088         
5089         var cfg = {
5090             tag : this.tag || 'div',
5091             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5092         };
5093         if (['light','white'].indexOf(this.weight) > -1) {
5094             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5095         }
5096         cfg.cls += ' bg-' + this.weight;
5097         
5098         if (this.inverse) {
5099             cfg.cls += ' navbar-inverse';
5100             
5101         }
5102         
5103         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5104         
5105         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5106             return cfg;
5107         }
5108         
5109         
5110     
5111         
5112         cfg.cn = [
5113             {
5114                 cls: 'nav nav-' + this.xtype,
5115                 tag : 'ul'
5116             }
5117         ];
5118         
5119          
5120         this.type = this.type || 'nav';
5121         if (['tabs','pills'].indexOf(this.type) != -1) {
5122             cfg.cn[0].cls += ' nav-' + this.type
5123         
5124         
5125         } else {
5126             if (this.type!=='nav') {
5127                 Roo.log('nav type must be nav/tabs/pills')
5128             }
5129             cfg.cn[0].cls += ' navbar-nav'
5130         }
5131         
5132         
5133         
5134         
5135         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5136             cfg.cn[0].cls += ' nav-' + this.arrangement;
5137         }
5138         
5139         
5140         if (this.align === 'right') {
5141             cfg.cn[0].cls += ' navbar-right';
5142         }
5143         
5144         
5145         
5146         
5147         return cfg;
5148     
5149         
5150     }
5151     
5152     
5153     
5154 });
5155
5156
5157
5158  
5159
5160  
5161        /*
5162  * - LGPL
5163  *
5164  * navbar
5165  * navbar-fixed-top
5166  * navbar-expand-md  fixed-top 
5167  */
5168
5169 /**
5170  * @class Roo.bootstrap.NavHeaderbar
5171  * @extends Roo.bootstrap.NavSimplebar
5172  * Bootstrap Sidebar class
5173  *
5174  * @cfg {String} brand what is brand
5175  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5176  * @cfg {String} brand_href href of the brand
5177  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5178  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5179  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5180  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5181  * 
5182  * @constructor
5183  * Create a new Sidebar
5184  * @param {Object} config The config object
5185  */
5186
5187
5188 Roo.bootstrap.NavHeaderbar = function(config){
5189     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5190       
5191 };
5192
5193 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5194     
5195     position: '',
5196     brand: '',
5197     brand_href: false,
5198     srButton : true,
5199     autohide : false,
5200     desktopCenter : false,
5201    
5202     
5203     getAutoCreate : function(){
5204         
5205         var   cfg = {
5206             tag: this.nav || 'nav',
5207             cls: 'navbar navbar-expand-md',
5208             role: 'navigation',
5209             cn: []
5210         };
5211         
5212         var cn = cfg.cn;
5213         if (this.desktopCenter) {
5214             cn.push({cls : 'container', cn : []});
5215             cn = cn[0].cn;
5216         }
5217         
5218         if(this.srButton){
5219             var btn = {
5220                 tag: 'button',
5221                 type: 'button',
5222                 cls: 'navbar-toggle navbar-toggler',
5223                 'data-toggle': 'collapse',
5224                 cn: [
5225                     {
5226                         tag: 'span',
5227                         cls: 'sr-only',
5228                         html: 'Toggle navigation'
5229                     },
5230                     {
5231                         tag: 'span',
5232                         cls: 'icon-bar navbar-toggler-icon'
5233                     },
5234                     {
5235                         tag: 'span',
5236                         cls: 'icon-bar'
5237                     },
5238                     {
5239                         tag: 'span',
5240                         cls: 'icon-bar'
5241                     }
5242                 ]
5243             };
5244             
5245             cn.push( Roo.bootstrap.version == 4 ? btn : {
5246                 tag: 'div',
5247                 cls: 'navbar-header',
5248                 cn: [
5249                     btn
5250                 ]
5251             });
5252         }
5253         
5254         cn.push({
5255             tag: 'div',
5256             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5257             cn : []
5258         });
5259         
5260         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5261         
5262         if (['light','white'].indexOf(this.weight) > -1) {
5263             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5264         }
5265         cfg.cls += ' bg-' + this.weight;
5266         
5267         
5268         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5269             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5270             
5271             // tag can override this..
5272             
5273             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5274         }
5275         
5276         if (this.brand !== '') {
5277             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5278             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5279                 tag: 'a',
5280                 href: this.brand_href ? this.brand_href : '#',
5281                 cls: 'navbar-brand',
5282                 cn: [
5283                 this.brand
5284                 ]
5285             });
5286         }
5287         
5288         if(this.main){
5289             cfg.cls += ' main-nav';
5290         }
5291         
5292         
5293         return cfg;
5294
5295         
5296     },
5297     getHeaderChildContainer : function()
5298     {
5299         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5300             return this.el.select('.navbar-header',true).first();
5301         }
5302         
5303         return this.getChildContainer();
5304     },
5305     
5306     getChildContainer : function()
5307     {
5308          
5309         return this.el.select('.roo-navbar-collapse',true).first();
5310          
5311         
5312     },
5313     
5314     initEvents : function()
5315     {
5316         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5317         
5318         if (this.autohide) {
5319             
5320             var prevScroll = 0;
5321             var ft = this.el;
5322             
5323             Roo.get(document).on('scroll',function(e) {
5324                 var ns = Roo.get(document).getScroll().top;
5325                 var os = prevScroll;
5326                 prevScroll = ns;
5327                 
5328                 if(ns > os){
5329                     ft.removeClass('slideDown');
5330                     ft.addClass('slideUp');
5331                     return;
5332                 }
5333                 ft.removeClass('slideUp');
5334                 ft.addClass('slideDown');
5335                  
5336               
5337           },this);
5338         }
5339     }    
5340     
5341 });
5342
5343
5344
5345  
5346
5347  /*
5348  * - LGPL
5349  *
5350  * navbar
5351  * 
5352  */
5353
5354 /**
5355  * @class Roo.bootstrap.NavSidebar
5356  * @extends Roo.bootstrap.Navbar
5357  * Bootstrap Sidebar class
5358  * 
5359  * @constructor
5360  * Create a new Sidebar
5361  * @param {Object} config The config object
5362  */
5363
5364
5365 Roo.bootstrap.NavSidebar = function(config){
5366     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5367 };
5368
5369 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5370     
5371     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5372     
5373     getAutoCreate : function(){
5374         
5375         
5376         return  {
5377             tag: 'div',
5378             cls: 'sidebar sidebar-nav'
5379         };
5380     
5381         
5382     }
5383     
5384     
5385     
5386 });
5387
5388
5389
5390  
5391
5392  /*
5393  * - LGPL
5394  *
5395  * nav group
5396  * 
5397  */
5398
5399 /**
5400  * @class Roo.bootstrap.NavGroup
5401  * @extends Roo.bootstrap.Component
5402  * Bootstrap NavGroup class
5403  * @cfg {String} align (left|right)
5404  * @cfg {Boolean} inverse
5405  * @cfg {String} type (nav|pills|tab) default nav
5406  * @cfg {String} navId - reference Id for navbar.
5407
5408  * 
5409  * @constructor
5410  * Create a new nav group
5411  * @param {Object} config The config object
5412  */
5413
5414 Roo.bootstrap.NavGroup = function(config){
5415     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5416     this.navItems = [];
5417    
5418     Roo.bootstrap.NavGroup.register(this);
5419      this.addEvents({
5420         /**
5421              * @event changed
5422              * Fires when the active item changes
5423              * @param {Roo.bootstrap.NavGroup} this
5424              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5425              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5426          */
5427         'changed': true
5428      });
5429     
5430 };
5431
5432 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5433     
5434     align: '',
5435     inverse: false,
5436     form: false,
5437     type: 'nav',
5438     navId : '',
5439     // private
5440     
5441     navItems : false, 
5442     
5443     getAutoCreate : function()
5444     {
5445         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5446         
5447         cfg = {
5448             tag : 'ul',
5449             cls: 'nav' 
5450         };
5451         if (Roo.bootstrap.version == 4) {
5452             if (['tabs','pills'].indexOf(this.type) != -1) {
5453                 cfg.cls += ' nav-' + this.type; 
5454             } else {
5455                 // trying to remove so header bar can right align top?
5456                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5457                     // do not use on header bar... 
5458                     cfg.cls += ' navbar-nav';
5459                 }
5460             }
5461             
5462         } else {
5463             if (['tabs','pills'].indexOf(this.type) != -1) {
5464                 cfg.cls += ' nav-' + this.type
5465             } else {
5466                 if (this.type !== 'nav') {
5467                     Roo.log('nav type must be nav/tabs/pills')
5468                 }
5469                 cfg.cls += ' navbar-nav'
5470             }
5471         }
5472         
5473         if (this.parent() && this.parent().sidebar) {
5474             cfg = {
5475                 tag: 'ul',
5476                 cls: 'dashboard-menu sidebar-menu'
5477             };
5478             
5479             return cfg;
5480         }
5481         
5482         if (this.form === true) {
5483             cfg = {
5484                 tag: 'form',
5485                 cls: 'navbar-form form-inline'
5486             };
5487             //nav navbar-right ml-md-auto
5488             if (this.align === 'right') {
5489                 cfg.cls += ' navbar-right ml-md-auto';
5490             } else {
5491                 cfg.cls += ' navbar-left';
5492             }
5493         }
5494         
5495         if (this.align === 'right') {
5496             cfg.cls += ' navbar-right ml-md-auto';
5497         } else {
5498             cfg.cls += ' mr-auto';
5499         }
5500         
5501         if (this.inverse) {
5502             cfg.cls += ' navbar-inverse';
5503             
5504         }
5505         
5506         
5507         return cfg;
5508     },
5509     /**
5510     * sets the active Navigation item
5511     * @param {Roo.bootstrap.NavItem} the new current navitem
5512     */
5513     setActiveItem : function(item)
5514     {
5515         var prev = false;
5516         Roo.each(this.navItems, function(v){
5517             if (v == item) {
5518                 return ;
5519             }
5520             if (v.isActive()) {
5521                 v.setActive(false, true);
5522                 prev = v;
5523                 
5524             }
5525             
5526         });
5527
5528         item.setActive(true, true);
5529         this.fireEvent('changed', this, item, prev);
5530         
5531         
5532     },
5533     /**
5534     * gets the active Navigation item
5535     * @return {Roo.bootstrap.NavItem} the current navitem
5536     */
5537     getActive : function()
5538     {
5539         
5540         var prev = false;
5541         Roo.each(this.navItems, function(v){
5542             
5543             if (v.isActive()) {
5544                 prev = v;
5545                 
5546             }
5547             
5548         });
5549         return prev;
5550     },
5551     
5552     indexOfNav : function()
5553     {
5554         
5555         var prev = false;
5556         Roo.each(this.navItems, function(v,i){
5557             
5558             if (v.isActive()) {
5559                 prev = i;
5560                 
5561             }
5562             
5563         });
5564         return prev;
5565     },
5566     /**
5567     * adds a Navigation item
5568     * @param {Roo.bootstrap.NavItem} the navitem to add
5569     */
5570     addItem : function(cfg)
5571     {
5572         if (this.form && Roo.bootstrap.version == 4) {
5573             cfg.tag = 'div';
5574         }
5575         var cn = new Roo.bootstrap.NavItem(cfg);
5576         this.register(cn);
5577         cn.parentId = this.id;
5578         cn.onRender(this.el, null);
5579         return cn;
5580     },
5581     /**
5582     * register a Navigation item
5583     * @param {Roo.bootstrap.NavItem} the navitem to add
5584     */
5585     register : function(item)
5586     {
5587         this.navItems.push( item);
5588         item.navId = this.navId;
5589     
5590     },
5591     
5592     /**
5593     * clear all the Navigation item
5594     */
5595    
5596     clearAll : function()
5597     {
5598         this.navItems = [];
5599         this.el.dom.innerHTML = '';
5600     },
5601     
5602     getNavItem: function(tabId)
5603     {
5604         var ret = false;
5605         Roo.each(this.navItems, function(e) {
5606             if (e.tabId == tabId) {
5607                ret =  e;
5608                return false;
5609             }
5610             return true;
5611             
5612         });
5613         return ret;
5614     },
5615     
5616     setActiveNext : function()
5617     {
5618         var i = this.indexOfNav(this.getActive());
5619         if (i > this.navItems.length) {
5620             return;
5621         }
5622         this.setActiveItem(this.navItems[i+1]);
5623     },
5624     setActivePrev : function()
5625     {
5626         var i = this.indexOfNav(this.getActive());
5627         if (i  < 1) {
5628             return;
5629         }
5630         this.setActiveItem(this.navItems[i-1]);
5631     },
5632     clearWasActive : function(except) {
5633         Roo.each(this.navItems, function(e) {
5634             if (e.tabId != except.tabId && e.was_active) {
5635                e.was_active = false;
5636                return false;
5637             }
5638             return true;
5639             
5640         });
5641     },
5642     getWasActive : function ()
5643     {
5644         var r = false;
5645         Roo.each(this.navItems, function(e) {
5646             if (e.was_active) {
5647                r = e;
5648                return false;
5649             }
5650             return true;
5651             
5652         });
5653         return r;
5654     }
5655     
5656     
5657 });
5658
5659  
5660 Roo.apply(Roo.bootstrap.NavGroup, {
5661     
5662     groups: {},
5663      /**
5664     * register a Navigation Group
5665     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5666     */
5667     register : function(navgrp)
5668     {
5669         this.groups[navgrp.navId] = navgrp;
5670         
5671     },
5672     /**
5673     * fetch a Navigation Group based on the navigation ID
5674     * @param {string} the navgroup to add
5675     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5676     */
5677     get: function(navId) {
5678         if (typeof(this.groups[navId]) == 'undefined') {
5679             return false;
5680             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5681         }
5682         return this.groups[navId] ;
5683     }
5684     
5685     
5686     
5687 });
5688
5689  /*
5690  * - LGPL
5691  *
5692  * row
5693  * 
5694  */
5695
5696 /**
5697  * @class Roo.bootstrap.NavItem
5698  * @extends Roo.bootstrap.Component
5699  * Bootstrap Navbar.NavItem class
5700  * @cfg {String} href  link to
5701  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5702
5703  * @cfg {String} html content of button
5704  * @cfg {String} badge text inside badge
5705  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5706  * @cfg {String} glyphicon DEPRICATED - use fa
5707  * @cfg {String} icon DEPRICATED - use fa
5708  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5709  * @cfg {Boolean} active Is item active
5710  * @cfg {Boolean} disabled Is item disabled
5711  
5712  * @cfg {Boolean} preventDefault (true | false) default false
5713  * @cfg {String} tabId the tab that this item activates.
5714  * @cfg {String} tagtype (a|span) render as a href or span?
5715  * @cfg {Boolean} animateRef (true|false) link to element default false  
5716   
5717  * @constructor
5718  * Create a new Navbar Item
5719  * @param {Object} config The config object
5720  */
5721 Roo.bootstrap.NavItem = function(config){
5722     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5723     this.addEvents({
5724         // raw events
5725         /**
5726          * @event click
5727          * The raw click event for the entire grid.
5728          * @param {Roo.EventObject} e
5729          */
5730         "click" : true,
5731          /**
5732             * @event changed
5733             * Fires when the active item active state changes
5734             * @param {Roo.bootstrap.NavItem} this
5735             * @param {boolean} state the new state
5736              
5737          */
5738         'changed': true,
5739         /**
5740             * @event scrollto
5741             * Fires when scroll to element
5742             * @param {Roo.bootstrap.NavItem} this
5743             * @param {Object} options
5744             * @param {Roo.EventObject} e
5745              
5746          */
5747         'scrollto': true
5748     });
5749    
5750 };
5751
5752 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5753     
5754     href: false,
5755     html: '',
5756     badge: '',
5757     icon: false,
5758     fa : false,
5759     glyphicon: false,
5760     active: false,
5761     preventDefault : false,
5762     tabId : false,
5763     tagtype : 'a',
5764     tag: 'li',
5765     disabled : false,
5766     animateRef : false,
5767     was_active : false,
5768     button_weight : '',
5769     button_outline : false,
5770     
5771     navLink: false,
5772     
5773     getAutoCreate : function(){
5774          
5775         var cfg = {
5776             tag: this.tag,
5777             cls: 'nav-item'
5778         };
5779         
5780         if (this.active) {
5781             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5782         }
5783         if (this.disabled) {
5784             cfg.cls += ' disabled';
5785         }
5786         
5787         // BS4 only?
5788         if (this.button_weight.length) {
5789             cfg.tag = this.href ? 'a' : 'button';
5790             cfg.html = this.html || '';
5791             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5792             if (this.href) {
5793                 cfg.href = this.href;
5794             }
5795             if (this.fa) {
5796                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5797             }
5798             
5799             // menu .. should add dropdown-menu class - so no need for carat..
5800             
5801             if (this.badge !== '') {
5802                  
5803                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5804             }
5805             return cfg;
5806         }
5807         
5808         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5809             cfg.cn = [
5810                 {
5811                     tag: this.tagtype,
5812                     href : this.href || "#",
5813                     html: this.html || ''
5814                 }
5815             ];
5816             if (this.tagtype == 'a') {
5817                 cfg.cn[0].cls = 'nav-link';
5818             }
5819             if (this.icon) {
5820                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5821             }
5822             if (this.fa) {
5823                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5824             }
5825             if(this.glyphicon) {
5826                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5827             }
5828             
5829             if (this.menu) {
5830                 
5831                 cfg.cn[0].html += " <span class='caret'></span>";
5832              
5833             }
5834             
5835             if (this.badge !== '') {
5836                  
5837                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5838             }
5839         }
5840         
5841         
5842         
5843         return cfg;
5844     },
5845     onRender : function(ct, position)
5846     {
5847        // Roo.log("Call onRender: " + this.xtype);
5848         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5849             this.tag = 'div';
5850         }
5851         
5852         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5853         this.navLink = this.el.select('.nav-link',true).first();
5854         return ret;
5855     },
5856       
5857     
5858     initEvents: function() 
5859     {
5860         if (typeof (this.menu) != 'undefined') {
5861             this.menu.parentType = this.xtype;
5862             this.menu.triggerEl = this.el;
5863             this.menu = this.addxtype(Roo.apply({}, this.menu));
5864         }
5865         
5866         this.el.select('a',true).on('click', this.onClick, this);
5867         
5868         if(this.tagtype == 'span'){
5869             this.el.select('span',true).on('click', this.onClick, this);
5870         }
5871        
5872         // at this point parent should be available..
5873         this.parent().register(this);
5874     },
5875     
5876     onClick : function(e)
5877     {
5878         if (e.getTarget('.dropdown-menu-item')) {
5879             // did you click on a menu itemm.... - then don't trigger onclick..
5880             return;
5881         }
5882         
5883         if(
5884                 this.preventDefault || 
5885                 this.href == '#' 
5886         ){
5887             Roo.log("NavItem - prevent Default?");
5888             e.preventDefault();
5889         }
5890         
5891         if (this.disabled) {
5892             return;
5893         }
5894         
5895         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5896         if (tg && tg.transition) {
5897             Roo.log("waiting for the transitionend");
5898             return;
5899         }
5900         
5901         
5902         
5903         //Roo.log("fire event clicked");
5904         if(this.fireEvent('click', this, e) === false){
5905             return;
5906         };
5907         
5908         if(this.tagtype == 'span'){
5909             return;
5910         }
5911         
5912         //Roo.log(this.href);
5913         var ael = this.el.select('a',true).first();
5914         //Roo.log(ael);
5915         
5916         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5917             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5918             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5919                 return; // ignore... - it's a 'hash' to another page.
5920             }
5921             Roo.log("NavItem - prevent Default?");
5922             e.preventDefault();
5923             this.scrollToElement(e);
5924         }
5925         
5926         
5927         var p =  this.parent();
5928    
5929         if (['tabs','pills'].indexOf(p.type)!==-1) {
5930             if (typeof(p.setActiveItem) !== 'undefined') {
5931                 p.setActiveItem(this);
5932             }
5933         }
5934         
5935         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5936         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5937             // remove the collapsed menu expand...
5938             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5939         }
5940     },
5941     
5942     isActive: function () {
5943         return this.active
5944     },
5945     setActive : function(state, fire, is_was_active)
5946     {
5947         if (this.active && !state && this.navId) {
5948             this.was_active = true;
5949             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5950             if (nv) {
5951                 nv.clearWasActive(this);
5952             }
5953             
5954         }
5955         this.active = state;
5956         
5957         if (!state ) {
5958             this.el.removeClass('active');
5959             this.navLink ? this.navLink.removeClass('active') : false;
5960         } else if (!this.el.hasClass('active')) {
5961             
5962             this.el.addClass('active');
5963             if (Roo.bootstrap.version == 4 && this.navLink ) {
5964                 this.navLink.addClass('active');
5965             }
5966             
5967         }
5968         if (fire) {
5969             this.fireEvent('changed', this, state);
5970         }
5971         
5972         // show a panel if it's registered and related..
5973         
5974         if (!this.navId || !this.tabId || !state || is_was_active) {
5975             return;
5976         }
5977         
5978         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5979         if (!tg) {
5980             return;
5981         }
5982         var pan = tg.getPanelByName(this.tabId);
5983         if (!pan) {
5984             return;
5985         }
5986         // if we can not flip to new panel - go back to old nav highlight..
5987         if (false == tg.showPanel(pan)) {
5988             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5989             if (nv) {
5990                 var onav = nv.getWasActive();
5991                 if (onav) {
5992                     onav.setActive(true, false, true);
5993                 }
5994             }
5995             
5996         }
5997         
5998         
5999         
6000     },
6001      // this should not be here...
6002     setDisabled : function(state)
6003     {
6004         this.disabled = state;
6005         if (!state ) {
6006             this.el.removeClass('disabled');
6007         } else if (!this.el.hasClass('disabled')) {
6008             this.el.addClass('disabled');
6009         }
6010         
6011     },
6012     
6013     /**
6014      * Fetch the element to display the tooltip on.
6015      * @return {Roo.Element} defaults to this.el
6016      */
6017     tooltipEl : function()
6018     {
6019         return this.el.select('' + this.tagtype + '', true).first();
6020     },
6021     
6022     scrollToElement : function(e)
6023     {
6024         var c = document.body;
6025         
6026         /*
6027          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6028          */
6029         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6030             c = document.documentElement;
6031         }
6032         
6033         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6034         
6035         if(!target){
6036             return;
6037         }
6038
6039         var o = target.calcOffsetsTo(c);
6040         
6041         var options = {
6042             target : target,
6043             value : o[1]
6044         };
6045         
6046         this.fireEvent('scrollto', this, options, e);
6047         
6048         Roo.get(c).scrollTo('top', options.value, true);
6049         
6050         return;
6051     }
6052 });
6053  
6054
6055  /*
6056  * - LGPL
6057  *
6058  * sidebar item
6059  *
6060  *  li
6061  *    <span> icon </span>
6062  *    <span> text </span>
6063  *    <span>badge </span>
6064  */
6065
6066 /**
6067  * @class Roo.bootstrap.NavSidebarItem
6068  * @extends Roo.bootstrap.NavItem
6069  * Bootstrap Navbar.NavSidebarItem class
6070  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6071  * {Boolean} open is the menu open
6072  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6073  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6074  * {String} buttonSize (sm|md|lg)the extra classes for the button
6075  * {Boolean} showArrow show arrow next to the text (default true)
6076  * @constructor
6077  * Create a new Navbar Button
6078  * @param {Object} config The config object
6079  */
6080 Roo.bootstrap.NavSidebarItem = function(config){
6081     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6082     this.addEvents({
6083         // raw events
6084         /**
6085          * @event click
6086          * The raw click event for the entire grid.
6087          * @param {Roo.EventObject} e
6088          */
6089         "click" : true,
6090          /**
6091             * @event changed
6092             * Fires when the active item active state changes
6093             * @param {Roo.bootstrap.NavSidebarItem} this
6094             * @param {boolean} state the new state
6095              
6096          */
6097         'changed': true
6098     });
6099    
6100 };
6101
6102 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6103     
6104     badgeWeight : 'default',
6105     
6106     open: false,
6107     
6108     buttonView : false,
6109     
6110     buttonWeight : 'default',
6111     
6112     buttonSize : 'md',
6113     
6114     showArrow : true,
6115     
6116     getAutoCreate : function(){
6117         
6118         
6119         var a = {
6120                 tag: 'a',
6121                 href : this.href || '#',
6122                 cls: '',
6123                 html : '',
6124                 cn : []
6125         };
6126         
6127         if(this.buttonView){
6128             a = {
6129                 tag: 'button',
6130                 href : this.href || '#',
6131                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6132                 html : this.html,
6133                 cn : []
6134             };
6135         }
6136         
6137         var cfg = {
6138             tag: 'li',
6139             cls: '',
6140             cn: [ a ]
6141         };
6142         
6143         if (this.active) {
6144             cfg.cls += ' active';
6145         }
6146         
6147         if (this.disabled) {
6148             cfg.cls += ' disabled';
6149         }
6150         if (this.open) {
6151             cfg.cls += ' open x-open';
6152         }
6153         // left icon..
6154         if (this.glyphicon || this.icon) {
6155             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6156             a.cn.push({ tag : 'i', cls : c }) ;
6157         }
6158         
6159         if(!this.buttonView){
6160             var span = {
6161                 tag: 'span',
6162                 html : this.html || ''
6163             };
6164
6165             a.cn.push(span);
6166             
6167         }
6168         
6169         if (this.badge !== '') {
6170             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6171         }
6172         
6173         if (this.menu) {
6174             
6175             if(this.showArrow){
6176                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6177             }
6178             
6179             a.cls += ' dropdown-toggle treeview' ;
6180         }
6181         
6182         return cfg;
6183     },
6184     
6185     initEvents : function()
6186     { 
6187         if (typeof (this.menu) != 'undefined') {
6188             this.menu.parentType = this.xtype;
6189             this.menu.triggerEl = this.el;
6190             this.menu = this.addxtype(Roo.apply({}, this.menu));
6191         }
6192         
6193         this.el.on('click', this.onClick, this);
6194         
6195         if(this.badge !== ''){
6196             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6197         }
6198         
6199     },
6200     
6201     onClick : function(e)
6202     {
6203         if(this.disabled){
6204             e.preventDefault();
6205             return;
6206         }
6207         
6208         if(this.preventDefault){
6209             e.preventDefault();
6210         }
6211         
6212         this.fireEvent('click', this, e);
6213     },
6214     
6215     disable : function()
6216     {
6217         this.setDisabled(true);
6218     },
6219     
6220     enable : function()
6221     {
6222         this.setDisabled(false);
6223     },
6224     
6225     setDisabled : function(state)
6226     {
6227         if(this.disabled == state){
6228             return;
6229         }
6230         
6231         this.disabled = state;
6232         
6233         if (state) {
6234             this.el.addClass('disabled');
6235             return;
6236         }
6237         
6238         this.el.removeClass('disabled');
6239         
6240         return;
6241     },
6242     
6243     setActive : function(state)
6244     {
6245         if(this.active == state){
6246             return;
6247         }
6248         
6249         this.active = state;
6250         
6251         if (state) {
6252             this.el.addClass('active');
6253             return;
6254         }
6255         
6256         this.el.removeClass('active');
6257         
6258         return;
6259     },
6260     
6261     isActive: function () 
6262     {
6263         return this.active;
6264     },
6265     
6266     setBadge : function(str)
6267     {
6268         if(!this.badgeEl){
6269             return;
6270         }
6271         
6272         this.badgeEl.dom.innerHTML = str;
6273     }
6274     
6275    
6276      
6277  
6278 });
6279  
6280
6281  /*
6282  * - LGPL
6283  *
6284  * row
6285  * 
6286  */
6287
6288 /**
6289  * @class Roo.bootstrap.Row
6290  * @extends Roo.bootstrap.Component
6291  * Bootstrap Row class (contains columns...)
6292  * 
6293  * @constructor
6294  * Create a new Row
6295  * @param {Object} config The config object
6296  */
6297
6298 Roo.bootstrap.Row = function(config){
6299     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6300 };
6301
6302 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6303     
6304     getAutoCreate : function(){
6305        return {
6306             cls: 'row clearfix'
6307        };
6308     }
6309     
6310     
6311 });
6312
6313  
6314
6315  /*
6316  * - LGPL
6317  *
6318  * pagination
6319  * 
6320  */
6321
6322 /**
6323  * @class Roo.bootstrap.Pagination
6324  * @extends Roo.bootstrap.Component
6325  * Bootstrap Pagination class
6326  * @cfg {String} size xs | sm | md | lg
6327  * @cfg {Boolean} inverse false | true
6328  * 
6329  * @constructor
6330  * Create a new Pagination
6331  * @param {Object} config The config object
6332  */
6333
6334 Roo.bootstrap.Pagination = function(config){
6335     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6336 };
6337
6338 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6339     
6340     cls: false,
6341     size: false,
6342     inverse: false,
6343     
6344     getAutoCreate : function(){
6345         var cfg = {
6346             tag: 'ul',
6347                 cls: 'pagination'
6348         };
6349         if (this.inverse) {
6350             cfg.cls += ' inverse';
6351         }
6352         if (this.html) {
6353             cfg.html=this.html;
6354         }
6355         if (this.cls) {
6356             cfg.cls += " " + this.cls;
6357         }
6358         return cfg;
6359     }
6360    
6361 });
6362
6363  
6364
6365  /*
6366  * - LGPL
6367  *
6368  * Pagination item
6369  * 
6370  */
6371
6372
6373 /**
6374  * @class Roo.bootstrap.PaginationItem
6375  * @extends Roo.bootstrap.Component
6376  * Bootstrap PaginationItem class
6377  * @cfg {String} html text
6378  * @cfg {String} href the link
6379  * @cfg {Boolean} preventDefault (true | false) default true
6380  * @cfg {Boolean} active (true | false) default false
6381  * @cfg {Boolean} disabled default false
6382  * 
6383  * 
6384  * @constructor
6385  * Create a new PaginationItem
6386  * @param {Object} config The config object
6387  */
6388
6389
6390 Roo.bootstrap.PaginationItem = function(config){
6391     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6392     this.addEvents({
6393         // raw events
6394         /**
6395          * @event click
6396          * The raw click event for the entire grid.
6397          * @param {Roo.EventObject} e
6398          */
6399         "click" : true
6400     });
6401 };
6402
6403 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6404     
6405     href : false,
6406     html : false,
6407     preventDefault: true,
6408     active : false,
6409     cls : false,
6410     disabled: false,
6411     
6412     getAutoCreate : function(){
6413         var cfg= {
6414             tag: 'li',
6415             cn: [
6416                 {
6417                     tag : 'a',
6418                     href : this.href ? this.href : '#',
6419                     html : this.html ? this.html : ''
6420                 }
6421             ]
6422         };
6423         
6424         if(this.cls){
6425             cfg.cls = this.cls;
6426         }
6427         
6428         if(this.disabled){
6429             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6430         }
6431         
6432         if(this.active){
6433             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6434         }
6435         
6436         return cfg;
6437     },
6438     
6439     initEvents: function() {
6440         
6441         this.el.on('click', this.onClick, this);
6442         
6443     },
6444     onClick : function(e)
6445     {
6446         Roo.log('PaginationItem on click ');
6447         if(this.preventDefault){
6448             e.preventDefault();
6449         }
6450         
6451         if(this.disabled){
6452             return;
6453         }
6454         
6455         this.fireEvent('click', this, e);
6456     }
6457    
6458 });
6459
6460  
6461
6462  /*
6463  * - LGPL
6464  *
6465  * slider
6466  * 
6467  */
6468
6469
6470 /**
6471  * @class Roo.bootstrap.Slider
6472  * @extends Roo.bootstrap.Component
6473  * Bootstrap Slider class
6474  *    
6475  * @constructor
6476  * Create a new Slider
6477  * @param {Object} config The config object
6478  */
6479
6480 Roo.bootstrap.Slider = function(config){
6481     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6482 };
6483
6484 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6485     
6486     getAutoCreate : function(){
6487         
6488         var cfg = {
6489             tag: 'div',
6490             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6491             cn: [
6492                 {
6493                     tag: 'a',
6494                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6495                 }
6496             ]
6497         };
6498         
6499         return cfg;
6500     }
6501    
6502 });
6503
6504  /*
6505  * Based on:
6506  * Ext JS Library 1.1.1
6507  * Copyright(c) 2006-2007, Ext JS, LLC.
6508  *
6509  * Originally Released Under LGPL - original licence link has changed is not relivant.
6510  *
6511  * Fork - LGPL
6512  * <script type="text/javascript">
6513  */
6514  
6515
6516 /**
6517  * @class Roo.grid.ColumnModel
6518  * @extends Roo.util.Observable
6519  * This is the default implementation of a ColumnModel used by the Grid. It defines
6520  * the columns in the grid.
6521  * <br>Usage:<br>
6522  <pre><code>
6523  var colModel = new Roo.grid.ColumnModel([
6524         {header: "Ticker", width: 60, sortable: true, locked: true},
6525         {header: "Company Name", width: 150, sortable: true},
6526         {header: "Market Cap.", width: 100, sortable: true},
6527         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6528         {header: "Employees", width: 100, sortable: true, resizable: false}
6529  ]);
6530  </code></pre>
6531  * <p>
6532  
6533  * The config options listed for this class are options which may appear in each
6534  * individual column definition.
6535  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6536  * @constructor
6537  * @param {Object} config An Array of column config objects. See this class's
6538  * config objects for details.
6539 */
6540 Roo.grid.ColumnModel = function(config){
6541         /**
6542      * The config passed into the constructor
6543      */
6544     this.config = config;
6545     this.lookup = {};
6546
6547     // if no id, create one
6548     // if the column does not have a dataIndex mapping,
6549     // map it to the order it is in the config
6550     for(var i = 0, len = config.length; i < len; i++){
6551         var c = config[i];
6552         if(typeof c.dataIndex == "undefined"){
6553             c.dataIndex = i;
6554         }
6555         if(typeof c.renderer == "string"){
6556             c.renderer = Roo.util.Format[c.renderer];
6557         }
6558         if(typeof c.id == "undefined"){
6559             c.id = Roo.id();
6560         }
6561         if(c.editor && c.editor.xtype){
6562             c.editor  = Roo.factory(c.editor, Roo.grid);
6563         }
6564         if(c.editor && c.editor.isFormField){
6565             c.editor = new Roo.grid.GridEditor(c.editor);
6566         }
6567         this.lookup[c.id] = c;
6568     }
6569
6570     /**
6571      * The width of columns which have no width specified (defaults to 100)
6572      * @type Number
6573      */
6574     this.defaultWidth = 100;
6575
6576     /**
6577      * Default sortable of columns which have no sortable specified (defaults to false)
6578      * @type Boolean
6579      */
6580     this.defaultSortable = false;
6581
6582     this.addEvents({
6583         /**
6584              * @event widthchange
6585              * Fires when the width of a column changes.
6586              * @param {ColumnModel} this
6587              * @param {Number} columnIndex The column index
6588              * @param {Number} newWidth The new width
6589              */
6590             "widthchange": true,
6591         /**
6592              * @event headerchange
6593              * Fires when the text of a header changes.
6594              * @param {ColumnModel} this
6595              * @param {Number} columnIndex The column index
6596              * @param {Number} newText The new header text
6597              */
6598             "headerchange": true,
6599         /**
6600              * @event hiddenchange
6601              * Fires when a column is hidden or "unhidden".
6602              * @param {ColumnModel} this
6603              * @param {Number} columnIndex The column index
6604              * @param {Boolean} hidden true if hidden, false otherwise
6605              */
6606             "hiddenchange": true,
6607             /**
6608          * @event columnmoved
6609          * Fires when a column is moved.
6610          * @param {ColumnModel} this
6611          * @param {Number} oldIndex
6612          * @param {Number} newIndex
6613          */
6614         "columnmoved" : true,
6615         /**
6616          * @event columlockchange
6617          * Fires when a column's locked state is changed
6618          * @param {ColumnModel} this
6619          * @param {Number} colIndex
6620          * @param {Boolean} locked true if locked
6621          */
6622         "columnlockchange" : true
6623     });
6624     Roo.grid.ColumnModel.superclass.constructor.call(this);
6625 };
6626 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6627     /**
6628      * @cfg {String} header The header text to display in the Grid view.
6629      */
6630     /**
6631      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6632      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6633      * specified, the column's index is used as an index into the Record's data Array.
6634      */
6635     /**
6636      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6637      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6638      */
6639     /**
6640      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6641      * Defaults to the value of the {@link #defaultSortable} property.
6642      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6643      */
6644     /**
6645      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6646      */
6647     /**
6648      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6649      */
6650     /**
6651      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6652      */
6653     /**
6654      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6655      */
6656     /**
6657      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6658      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6659      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6660      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6661      */
6662        /**
6663      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6664      */
6665     /**
6666      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6667      */
6668     /**
6669      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6670      */
6671     /**
6672      * @cfg {String} cursor (Optional)
6673      */
6674     /**
6675      * @cfg {String} tooltip (Optional)
6676      */
6677     /**
6678      * @cfg {Number} xs (Optional)
6679      */
6680     /**
6681      * @cfg {Number} sm (Optional)
6682      */
6683     /**
6684      * @cfg {Number} md (Optional)
6685      */
6686     /**
6687      * @cfg {Number} lg (Optional)
6688      */
6689     /**
6690      * Returns the id of the column at the specified index.
6691      * @param {Number} index The column index
6692      * @return {String} the id
6693      */
6694     getColumnId : function(index){
6695         return this.config[index].id;
6696     },
6697
6698     /**
6699      * Returns the column for a specified id.
6700      * @param {String} id The column id
6701      * @return {Object} the column
6702      */
6703     getColumnById : function(id){
6704         return this.lookup[id];
6705     },
6706
6707     
6708     /**
6709      * Returns the column for a specified dataIndex.
6710      * @param {String} dataIndex The column dataIndex
6711      * @return {Object|Boolean} the column or false if not found
6712      */
6713     getColumnByDataIndex: function(dataIndex){
6714         var index = this.findColumnIndex(dataIndex);
6715         return index > -1 ? this.config[index] : false;
6716     },
6717     
6718     /**
6719      * Returns the index for a specified column id.
6720      * @param {String} id The column id
6721      * @return {Number} the index, or -1 if not found
6722      */
6723     getIndexById : function(id){
6724         for(var i = 0, len = this.config.length; i < len; i++){
6725             if(this.config[i].id == id){
6726                 return i;
6727             }
6728         }
6729         return -1;
6730     },
6731     
6732     /**
6733      * Returns the index for a specified column dataIndex.
6734      * @param {String} dataIndex The column dataIndex
6735      * @return {Number} the index, or -1 if not found
6736      */
6737     
6738     findColumnIndex : function(dataIndex){
6739         for(var i = 0, len = this.config.length; i < len; i++){
6740             if(this.config[i].dataIndex == dataIndex){
6741                 return i;
6742             }
6743         }
6744         return -1;
6745     },
6746     
6747     
6748     moveColumn : function(oldIndex, newIndex){
6749         var c = this.config[oldIndex];
6750         this.config.splice(oldIndex, 1);
6751         this.config.splice(newIndex, 0, c);
6752         this.dataMap = null;
6753         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6754     },
6755
6756     isLocked : function(colIndex){
6757         return this.config[colIndex].locked === true;
6758     },
6759
6760     setLocked : function(colIndex, value, suppressEvent){
6761         if(this.isLocked(colIndex) == value){
6762             return;
6763         }
6764         this.config[colIndex].locked = value;
6765         if(!suppressEvent){
6766             this.fireEvent("columnlockchange", this, colIndex, value);
6767         }
6768     },
6769
6770     getTotalLockedWidth : function(){
6771         var totalWidth = 0;
6772         for(var i = 0; i < this.config.length; i++){
6773             if(this.isLocked(i) && !this.isHidden(i)){
6774                 this.totalWidth += this.getColumnWidth(i);
6775             }
6776         }
6777         return totalWidth;
6778     },
6779
6780     getLockedCount : function(){
6781         for(var i = 0, len = this.config.length; i < len; i++){
6782             if(!this.isLocked(i)){
6783                 return i;
6784             }
6785         }
6786         
6787         return this.config.length;
6788     },
6789
6790     /**
6791      * Returns the number of columns.
6792      * @return {Number}
6793      */
6794     getColumnCount : function(visibleOnly){
6795         if(visibleOnly === true){
6796             var c = 0;
6797             for(var i = 0, len = this.config.length; i < len; i++){
6798                 if(!this.isHidden(i)){
6799                     c++;
6800                 }
6801             }
6802             return c;
6803         }
6804         return this.config.length;
6805     },
6806
6807     /**
6808      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6809      * @param {Function} fn
6810      * @param {Object} scope (optional)
6811      * @return {Array} result
6812      */
6813     getColumnsBy : function(fn, scope){
6814         var r = [];
6815         for(var i = 0, len = this.config.length; i < len; i++){
6816             var c = this.config[i];
6817             if(fn.call(scope||this, c, i) === true){
6818                 r[r.length] = c;
6819             }
6820         }
6821         return r;
6822     },
6823
6824     /**
6825      * Returns true if the specified column is sortable.
6826      * @param {Number} col The column index
6827      * @return {Boolean}
6828      */
6829     isSortable : function(col){
6830         if(typeof this.config[col].sortable == "undefined"){
6831             return this.defaultSortable;
6832         }
6833         return this.config[col].sortable;
6834     },
6835
6836     /**
6837      * Returns the rendering (formatting) function defined for the column.
6838      * @param {Number} col The column index.
6839      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6840      */
6841     getRenderer : function(col){
6842         if(!this.config[col].renderer){
6843             return Roo.grid.ColumnModel.defaultRenderer;
6844         }
6845         return this.config[col].renderer;
6846     },
6847
6848     /**
6849      * Sets the rendering (formatting) function for a column.
6850      * @param {Number} col The column index
6851      * @param {Function} fn The function to use to process the cell's raw data
6852      * to return HTML markup for the grid view. The render function is called with
6853      * the following parameters:<ul>
6854      * <li>Data value.</li>
6855      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6856      * <li>css A CSS style string to apply to the table cell.</li>
6857      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6858      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6859      * <li>Row index</li>
6860      * <li>Column index</li>
6861      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6862      */
6863     setRenderer : function(col, fn){
6864         this.config[col].renderer = fn;
6865     },
6866
6867     /**
6868      * Returns the width for the specified column.
6869      * @param {Number} col The column index
6870      * @return {Number}
6871      */
6872     getColumnWidth : function(col){
6873         return this.config[col].width * 1 || this.defaultWidth;
6874     },
6875
6876     /**
6877      * Sets the width for a column.
6878      * @param {Number} col The column index
6879      * @param {Number} width The new width
6880      */
6881     setColumnWidth : function(col, width, suppressEvent){
6882         this.config[col].width = width;
6883         this.totalWidth = null;
6884         if(!suppressEvent){
6885              this.fireEvent("widthchange", this, col, width);
6886         }
6887     },
6888
6889     /**
6890      * Returns the total width of all columns.
6891      * @param {Boolean} includeHidden True to include hidden column widths
6892      * @return {Number}
6893      */
6894     getTotalWidth : function(includeHidden){
6895         if(!this.totalWidth){
6896             this.totalWidth = 0;
6897             for(var i = 0, len = this.config.length; i < len; i++){
6898                 if(includeHidden || !this.isHidden(i)){
6899                     this.totalWidth += this.getColumnWidth(i);
6900                 }
6901             }
6902         }
6903         return this.totalWidth;
6904     },
6905
6906     /**
6907      * Returns the header for the specified column.
6908      * @param {Number} col The column index
6909      * @return {String}
6910      */
6911     getColumnHeader : function(col){
6912         return this.config[col].header;
6913     },
6914
6915     /**
6916      * Sets the header for a column.
6917      * @param {Number} col The column index
6918      * @param {String} header The new header
6919      */
6920     setColumnHeader : function(col, header){
6921         this.config[col].header = header;
6922         this.fireEvent("headerchange", this, col, header);
6923     },
6924
6925     /**
6926      * Returns the tooltip for the specified column.
6927      * @param {Number} col The column index
6928      * @return {String}
6929      */
6930     getColumnTooltip : function(col){
6931             return this.config[col].tooltip;
6932     },
6933     /**
6934      * Sets the tooltip for a column.
6935      * @param {Number} col The column index
6936      * @param {String} tooltip The new tooltip
6937      */
6938     setColumnTooltip : function(col, tooltip){
6939             this.config[col].tooltip = tooltip;
6940     },
6941
6942     /**
6943      * Returns the dataIndex for the specified column.
6944      * @param {Number} col The column index
6945      * @return {Number}
6946      */
6947     getDataIndex : function(col){
6948         return this.config[col].dataIndex;
6949     },
6950
6951     /**
6952      * Sets the dataIndex for a column.
6953      * @param {Number} col The column index
6954      * @param {Number} dataIndex The new dataIndex
6955      */
6956     setDataIndex : function(col, dataIndex){
6957         this.config[col].dataIndex = dataIndex;
6958     },
6959
6960     
6961     
6962     /**
6963      * Returns true if the cell is editable.
6964      * @param {Number} colIndex The column index
6965      * @param {Number} rowIndex The row index - this is nto actually used..?
6966      * @return {Boolean}
6967      */
6968     isCellEditable : function(colIndex, rowIndex){
6969         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6970     },
6971
6972     /**
6973      * Returns the editor defined for the cell/column.
6974      * return false or null to disable editing.
6975      * @param {Number} colIndex The column index
6976      * @param {Number} rowIndex The row index
6977      * @return {Object}
6978      */
6979     getCellEditor : function(colIndex, rowIndex){
6980         return this.config[colIndex].editor;
6981     },
6982
6983     /**
6984      * Sets if a column is editable.
6985      * @param {Number} col The column index
6986      * @param {Boolean} editable True if the column is editable
6987      */
6988     setEditable : function(col, editable){
6989         this.config[col].editable = editable;
6990     },
6991
6992
6993     /**
6994      * Returns true if the column is hidden.
6995      * @param {Number} colIndex The column index
6996      * @return {Boolean}
6997      */
6998     isHidden : function(colIndex){
6999         return this.config[colIndex].hidden;
7000     },
7001
7002
7003     /**
7004      * Returns true if the column width cannot be changed
7005      */
7006     isFixed : function(colIndex){
7007         return this.config[colIndex].fixed;
7008     },
7009
7010     /**
7011      * Returns true if the column can be resized
7012      * @return {Boolean}
7013      */
7014     isResizable : function(colIndex){
7015         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7016     },
7017     /**
7018      * Sets if a column is hidden.
7019      * @param {Number} colIndex The column index
7020      * @param {Boolean} hidden True if the column is hidden
7021      */
7022     setHidden : function(colIndex, hidden){
7023         this.config[colIndex].hidden = hidden;
7024         this.totalWidth = null;
7025         this.fireEvent("hiddenchange", this, colIndex, hidden);
7026     },
7027
7028     /**
7029      * Sets the editor for a column.
7030      * @param {Number} col The column index
7031      * @param {Object} editor The editor object
7032      */
7033     setEditor : function(col, editor){
7034         this.config[col].editor = editor;
7035     }
7036 });
7037
7038 Roo.grid.ColumnModel.defaultRenderer = function(value)
7039 {
7040     if(typeof value == "object") {
7041         return value;
7042     }
7043         if(typeof value == "string" && value.length < 1){
7044             return "&#160;";
7045         }
7046     
7047         return String.format("{0}", value);
7048 };
7049
7050 // Alias for backwards compatibility
7051 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7052 /*
7053  * Based on:
7054  * Ext JS Library 1.1.1
7055  * Copyright(c) 2006-2007, Ext JS, LLC.
7056  *
7057  * Originally Released Under LGPL - original licence link has changed is not relivant.
7058  *
7059  * Fork - LGPL
7060  * <script type="text/javascript">
7061  */
7062  
7063 /**
7064  * @class Roo.LoadMask
7065  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7066  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7067  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7068  * element's UpdateManager load indicator and will be destroyed after the initial load.
7069  * @constructor
7070  * Create a new LoadMask
7071  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7072  * @param {Object} config The config object
7073  */
7074 Roo.LoadMask = function(el, config){
7075     this.el = Roo.get(el);
7076     Roo.apply(this, config);
7077     if(this.store){
7078         this.store.on('beforeload', this.onBeforeLoad, this);
7079         this.store.on('load', this.onLoad, this);
7080         this.store.on('loadexception', this.onLoadException, this);
7081         this.removeMask = false;
7082     }else{
7083         var um = this.el.getUpdateManager();
7084         um.showLoadIndicator = false; // disable the default indicator
7085         um.on('beforeupdate', this.onBeforeLoad, this);
7086         um.on('update', this.onLoad, this);
7087         um.on('failure', this.onLoad, this);
7088         this.removeMask = true;
7089     }
7090 };
7091
7092 Roo.LoadMask.prototype = {
7093     /**
7094      * @cfg {Boolean} removeMask
7095      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7096      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7097      */
7098     /**
7099      * @cfg {String} msg
7100      * The text to display in a centered loading message box (defaults to 'Loading...')
7101      */
7102     msg : 'Loading...',
7103     /**
7104      * @cfg {String} msgCls
7105      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7106      */
7107     msgCls : 'x-mask-loading',
7108
7109     /**
7110      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7111      * @type Boolean
7112      */
7113     disabled: false,
7114
7115     /**
7116      * Disables the mask to prevent it from being displayed
7117      */
7118     disable : function(){
7119        this.disabled = true;
7120     },
7121
7122     /**
7123      * Enables the mask so that it can be displayed
7124      */
7125     enable : function(){
7126         this.disabled = false;
7127     },
7128     
7129     onLoadException : function()
7130     {
7131         Roo.log(arguments);
7132         
7133         if (typeof(arguments[3]) != 'undefined') {
7134             Roo.MessageBox.alert("Error loading",arguments[3]);
7135         } 
7136         /*
7137         try {
7138             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7139                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7140             }   
7141         } catch(e) {
7142             
7143         }
7144         */
7145     
7146         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7147     },
7148     // private
7149     onLoad : function()
7150     {
7151         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7152     },
7153
7154     // private
7155     onBeforeLoad : function(){
7156         if(!this.disabled){
7157             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7158         }
7159     },
7160
7161     // private
7162     destroy : function(){
7163         if(this.store){
7164             this.store.un('beforeload', this.onBeforeLoad, this);
7165             this.store.un('load', this.onLoad, this);
7166             this.store.un('loadexception', this.onLoadException, this);
7167         }else{
7168             var um = this.el.getUpdateManager();
7169             um.un('beforeupdate', this.onBeforeLoad, this);
7170             um.un('update', this.onLoad, this);
7171             um.un('failure', this.onLoad, this);
7172         }
7173     }
7174 };/*
7175  * - LGPL
7176  *
7177  * table
7178  * 
7179  */
7180
7181 /**
7182  * @class Roo.bootstrap.Table
7183  * @extends Roo.bootstrap.Component
7184  * Bootstrap Table class
7185  * @cfg {String} cls table class
7186  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7187  * @cfg {String} bgcolor Specifies the background color for a table
7188  * @cfg {Number} border Specifies whether the table cells should have borders or not
7189  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7190  * @cfg {Number} cellspacing Specifies the space between cells
7191  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7192  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7193  * @cfg {String} sortable Specifies that the table should be sortable
7194  * @cfg {String} summary Specifies a summary of the content of a table
7195  * @cfg {Number} width Specifies the width of a table
7196  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7197  * 
7198  * @cfg {boolean} striped Should the rows be alternative striped
7199  * @cfg {boolean} bordered Add borders to the table
7200  * @cfg {boolean} hover Add hover highlighting
7201  * @cfg {boolean} condensed Format condensed
7202  * @cfg {boolean} responsive Format condensed
7203  * @cfg {Boolean} loadMask (true|false) default false
7204  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7205  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7206  * @cfg {Boolean} rowSelection (true|false) default false
7207  * @cfg {Boolean} cellSelection (true|false) default false
7208  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7209  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7210  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7211  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7212  
7213  * 
7214  * @constructor
7215  * Create a new Table
7216  * @param {Object} config The config object
7217  */
7218
7219 Roo.bootstrap.Table = function(config){
7220     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7221     
7222   
7223     
7224     // BC...
7225     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7226     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7227     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7228     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7229     
7230     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7231     if (this.sm) {
7232         this.sm.grid = this;
7233         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7234         this.sm = this.selModel;
7235         this.sm.xmodule = this.xmodule || false;
7236     }
7237     
7238     if (this.cm && typeof(this.cm.config) == 'undefined') {
7239         this.colModel = new Roo.grid.ColumnModel(this.cm);
7240         this.cm = this.colModel;
7241         this.cm.xmodule = this.xmodule || false;
7242     }
7243     if (this.store) {
7244         this.store= Roo.factory(this.store, Roo.data);
7245         this.ds = this.store;
7246         this.ds.xmodule = this.xmodule || false;
7247          
7248     }
7249     if (this.footer && this.store) {
7250         this.footer.dataSource = this.ds;
7251         this.footer = Roo.factory(this.footer);
7252     }
7253     
7254     /** @private */
7255     this.addEvents({
7256         /**
7257          * @event cellclick
7258          * Fires when a cell is clicked
7259          * @param {Roo.bootstrap.Table} this
7260          * @param {Roo.Element} el
7261          * @param {Number} rowIndex
7262          * @param {Number} columnIndex
7263          * @param {Roo.EventObject} e
7264          */
7265         "cellclick" : true,
7266         /**
7267          * @event celldblclick
7268          * Fires when a cell is double clicked
7269          * @param {Roo.bootstrap.Table} this
7270          * @param {Roo.Element} el
7271          * @param {Number} rowIndex
7272          * @param {Number} columnIndex
7273          * @param {Roo.EventObject} e
7274          */
7275         "celldblclick" : true,
7276         /**
7277          * @event rowclick
7278          * Fires when a row is clicked
7279          * @param {Roo.bootstrap.Table} this
7280          * @param {Roo.Element} el
7281          * @param {Number} rowIndex
7282          * @param {Roo.EventObject} e
7283          */
7284         "rowclick" : true,
7285         /**
7286          * @event rowdblclick
7287          * Fires when a row is double clicked
7288          * @param {Roo.bootstrap.Table} this
7289          * @param {Roo.Element} el
7290          * @param {Number} rowIndex
7291          * @param {Roo.EventObject} e
7292          */
7293         "rowdblclick" : true,
7294         /**
7295          * @event mouseover
7296          * Fires when a mouseover occur
7297          * @param {Roo.bootstrap.Table} this
7298          * @param {Roo.Element} el
7299          * @param {Number} rowIndex
7300          * @param {Number} columnIndex
7301          * @param {Roo.EventObject} e
7302          */
7303         "mouseover" : true,
7304         /**
7305          * @event mouseout
7306          * Fires when a mouseout occur
7307          * @param {Roo.bootstrap.Table} this
7308          * @param {Roo.Element} el
7309          * @param {Number} rowIndex
7310          * @param {Number} columnIndex
7311          * @param {Roo.EventObject} e
7312          */
7313         "mouseout" : true,
7314         /**
7315          * @event rowclass
7316          * Fires when a row is rendered, so you can change add a style to it.
7317          * @param {Roo.bootstrap.Table} this
7318          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7319          */
7320         'rowclass' : true,
7321           /**
7322          * @event rowsrendered
7323          * Fires when all the  rows have been rendered
7324          * @param {Roo.bootstrap.Table} this
7325          */
7326         'rowsrendered' : true,
7327         /**
7328          * @event contextmenu
7329          * The raw contextmenu event for the entire grid.
7330          * @param {Roo.EventObject} e
7331          */
7332         "contextmenu" : true,
7333         /**
7334          * @event rowcontextmenu
7335          * Fires when a row is right clicked
7336          * @param {Roo.bootstrap.Table} this
7337          * @param {Number} rowIndex
7338          * @param {Roo.EventObject} e
7339          */
7340         "rowcontextmenu" : true,
7341         /**
7342          * @event cellcontextmenu
7343          * Fires when a cell is right clicked
7344          * @param {Roo.bootstrap.Table} this
7345          * @param {Number} rowIndex
7346          * @param {Number} cellIndex
7347          * @param {Roo.EventObject} e
7348          */
7349          "cellcontextmenu" : true,
7350          /**
7351          * @event headercontextmenu
7352          * Fires when a header is right clicked
7353          * @param {Roo.bootstrap.Table} this
7354          * @param {Number} columnIndex
7355          * @param {Roo.EventObject} e
7356          */
7357         "headercontextmenu" : true
7358     });
7359 };
7360
7361 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7362     
7363     cls: false,
7364     align: false,
7365     bgcolor: false,
7366     border: false,
7367     cellpadding: false,
7368     cellspacing: false,
7369     frame: false,
7370     rules: false,
7371     sortable: false,
7372     summary: false,
7373     width: false,
7374     striped : false,
7375     scrollBody : false,
7376     bordered: false,
7377     hover:  false,
7378     condensed : false,
7379     responsive : false,
7380     sm : false,
7381     cm : false,
7382     store : false,
7383     loadMask : false,
7384     footerShow : true,
7385     headerShow : true,
7386   
7387     rowSelection : false,
7388     cellSelection : false,
7389     layout : false,
7390     
7391     // Roo.Element - the tbody
7392     mainBody: false,
7393     // Roo.Element - thead element
7394     mainHead: false,
7395     
7396     container: false, // used by gridpanel...
7397     
7398     lazyLoad : false,
7399     
7400     CSS : Roo.util.CSS,
7401     
7402     auto_hide_footer : false,
7403     
7404     getAutoCreate : function()
7405     {
7406         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7407         
7408         cfg = {
7409             tag: 'table',
7410             cls : 'table',
7411             cn : []
7412         };
7413         if (this.scrollBody) {
7414             cfg.cls += ' table-body-fixed';
7415         }    
7416         if (this.striped) {
7417             cfg.cls += ' table-striped';
7418         }
7419         
7420         if (this.hover) {
7421             cfg.cls += ' table-hover';
7422         }
7423         if (this.bordered) {
7424             cfg.cls += ' table-bordered';
7425         }
7426         if (this.condensed) {
7427             cfg.cls += ' table-condensed';
7428         }
7429         if (this.responsive) {
7430             cfg.cls += ' table-responsive';
7431         }
7432         
7433         if (this.cls) {
7434             cfg.cls+=  ' ' +this.cls;
7435         }
7436         
7437         // this lot should be simplifed...
7438         var _t = this;
7439         var cp = [
7440             'align',
7441             'bgcolor',
7442             'border',
7443             'cellpadding',
7444             'cellspacing',
7445             'frame',
7446             'rules',
7447             'sortable',
7448             'summary',
7449             'width'
7450         ].forEach(function(k) {
7451             if (_t[k]) {
7452                 cfg[k] = _t[k];
7453             }
7454         });
7455         
7456         
7457         if (this.layout) {
7458             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7459         }
7460         
7461         if(this.store || this.cm){
7462             if(this.headerShow){
7463                 cfg.cn.push(this.renderHeader());
7464             }
7465             
7466             cfg.cn.push(this.renderBody());
7467             
7468             if(this.footerShow){
7469                 cfg.cn.push(this.renderFooter());
7470             }
7471             // where does this come from?
7472             //cfg.cls+=  ' TableGrid';
7473         }
7474         
7475         return { cn : [ cfg ] };
7476     },
7477     
7478     initEvents : function()
7479     {   
7480         if(!this.store || !this.cm){
7481             return;
7482         }
7483         if (this.selModel) {
7484             this.selModel.initEvents();
7485         }
7486         
7487         
7488         //Roo.log('initEvents with ds!!!!');
7489         
7490         this.mainBody = this.el.select('tbody', true).first();
7491         this.mainHead = this.el.select('thead', true).first();
7492         this.mainFoot = this.el.select('tfoot', true).first();
7493         
7494         
7495         
7496         var _this = this;
7497         
7498         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7499             e.on('click', _this.sort, _this);
7500         });
7501         
7502         this.mainBody.on("click", this.onClick, this);
7503         this.mainBody.on("dblclick", this.onDblClick, this);
7504         
7505         // why is this done????? = it breaks dialogs??
7506         //this.parent().el.setStyle('position', 'relative');
7507         
7508         
7509         if (this.footer) {
7510             this.footer.parentId = this.id;
7511             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7512             
7513             if(this.lazyLoad){
7514                 this.el.select('tfoot tr td').first().addClass('hide');
7515             }
7516         } 
7517         
7518         if(this.loadMask) {
7519             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7520         }
7521         
7522         this.store.on('load', this.onLoad, this);
7523         this.store.on('beforeload', this.onBeforeLoad, this);
7524         this.store.on('update', this.onUpdate, this);
7525         this.store.on('add', this.onAdd, this);
7526         this.store.on("clear", this.clear, this);
7527         
7528         this.el.on("contextmenu", this.onContextMenu, this);
7529         
7530         this.mainBody.on('scroll', this.onBodyScroll, this);
7531         
7532         this.cm.on("headerchange", this.onHeaderChange, this);
7533         
7534         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7535         
7536     },
7537     
7538     onContextMenu : function(e, t)
7539     {
7540         this.processEvent("contextmenu", e);
7541     },
7542     
7543     processEvent : function(name, e)
7544     {
7545         if (name != 'touchstart' ) {
7546             this.fireEvent(name, e);    
7547         }
7548         
7549         var t = e.getTarget();
7550         
7551         var cell = Roo.get(t);
7552         
7553         if(!cell){
7554             return;
7555         }
7556         
7557         if(cell.findParent('tfoot', false, true)){
7558             return;
7559         }
7560         
7561         if(cell.findParent('thead', false, true)){
7562             
7563             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7564                 cell = Roo.get(t).findParent('th', false, true);
7565                 if (!cell) {
7566                     Roo.log("failed to find th in thead?");
7567                     Roo.log(e.getTarget());
7568                     return;
7569                 }
7570             }
7571             
7572             var cellIndex = cell.dom.cellIndex;
7573             
7574             var ename = name == 'touchstart' ? 'click' : name;
7575             this.fireEvent("header" + ename, this, cellIndex, e);
7576             
7577             return;
7578         }
7579         
7580         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7581             cell = Roo.get(t).findParent('td', false, true);
7582             if (!cell) {
7583                 Roo.log("failed to find th in tbody?");
7584                 Roo.log(e.getTarget());
7585                 return;
7586             }
7587         }
7588         
7589         var row = cell.findParent('tr', false, true);
7590         var cellIndex = cell.dom.cellIndex;
7591         var rowIndex = row.dom.rowIndex - 1;
7592         
7593         if(row !== false){
7594             
7595             this.fireEvent("row" + name, this, rowIndex, e);
7596             
7597             if(cell !== false){
7598             
7599                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7600             }
7601         }
7602         
7603     },
7604     
7605     onMouseover : function(e, el)
7606     {
7607         var cell = Roo.get(el);
7608         
7609         if(!cell){
7610             return;
7611         }
7612         
7613         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7614             cell = cell.findParent('td', false, true);
7615         }
7616         
7617         var row = cell.findParent('tr', false, true);
7618         var cellIndex = cell.dom.cellIndex;
7619         var rowIndex = row.dom.rowIndex - 1; // start from 0
7620         
7621         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7622         
7623     },
7624     
7625     onMouseout : function(e, el)
7626     {
7627         var cell = Roo.get(el);
7628         
7629         if(!cell){
7630             return;
7631         }
7632         
7633         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7634             cell = cell.findParent('td', false, true);
7635         }
7636         
7637         var row = cell.findParent('tr', false, true);
7638         var cellIndex = cell.dom.cellIndex;
7639         var rowIndex = row.dom.rowIndex - 1; // start from 0
7640         
7641         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7642         
7643     },
7644     
7645     onClick : function(e, el)
7646     {
7647         var cell = Roo.get(el);
7648         
7649         if(!cell || (!this.cellSelection && !this.rowSelection)){
7650             return;
7651         }
7652         
7653         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7654             cell = cell.findParent('td', false, true);
7655         }
7656         
7657         if(!cell || typeof(cell) == 'undefined'){
7658             return;
7659         }
7660         
7661         var row = cell.findParent('tr', false, true);
7662         
7663         if(!row || typeof(row) == 'undefined'){
7664             return;
7665         }
7666         
7667         var cellIndex = cell.dom.cellIndex;
7668         var rowIndex = this.getRowIndex(row);
7669         
7670         // why??? - should these not be based on SelectionModel?
7671         if(this.cellSelection){
7672             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7673         }
7674         
7675         if(this.rowSelection){
7676             this.fireEvent('rowclick', this, row, rowIndex, e);
7677         }
7678         
7679         
7680     },
7681         
7682     onDblClick : function(e,el)
7683     {
7684         var cell = Roo.get(el);
7685         
7686         if(!cell || (!this.cellSelection && !this.rowSelection)){
7687             return;
7688         }
7689         
7690         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7691             cell = cell.findParent('td', false, true);
7692         }
7693         
7694         if(!cell || typeof(cell) == 'undefined'){
7695             return;
7696         }
7697         
7698         var row = cell.findParent('tr', false, true);
7699         
7700         if(!row || typeof(row) == 'undefined'){
7701             return;
7702         }
7703         
7704         var cellIndex = cell.dom.cellIndex;
7705         var rowIndex = this.getRowIndex(row);
7706         
7707         if(this.cellSelection){
7708             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7709         }
7710         
7711         if(this.rowSelection){
7712             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7713         }
7714     },
7715     
7716     sort : function(e,el)
7717     {
7718         var col = Roo.get(el);
7719         
7720         if(!col.hasClass('sortable')){
7721             return;
7722         }
7723         
7724         var sort = col.attr('sort');
7725         var dir = 'ASC';
7726         
7727         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7728             dir = 'DESC';
7729         }
7730         
7731         this.store.sortInfo = {field : sort, direction : dir};
7732         
7733         if (this.footer) {
7734             Roo.log("calling footer first");
7735             this.footer.onClick('first');
7736         } else {
7737         
7738             this.store.load({ params : { start : 0 } });
7739         }
7740     },
7741     
7742     renderHeader : function()
7743     {
7744         var header = {
7745             tag: 'thead',
7746             cn : []
7747         };
7748         
7749         var cm = this.cm;
7750         this.totalWidth = 0;
7751         
7752         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7753             
7754             var config = cm.config[i];
7755             
7756             var c = {
7757                 tag: 'th',
7758                 cls : 'x-hcol-' + i,
7759                 style : '',
7760                 html: cm.getColumnHeader(i)
7761             };
7762             
7763             var hh = '';
7764             
7765             if(typeof(config.sortable) != 'undefined' && config.sortable){
7766                 c.cls = 'sortable';
7767                 c.html = '<i class="glyphicon"></i>' + c.html;
7768             }
7769             
7770             // could use BS4 hidden-..-down 
7771             
7772             if(typeof(config.lgHeader) != 'undefined'){
7773                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7774             }
7775             
7776             if(typeof(config.mdHeader) != 'undefined'){
7777                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7778             }
7779             
7780             if(typeof(config.smHeader) != 'undefined'){
7781                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7782             }
7783             
7784             if(typeof(config.xsHeader) != 'undefined'){
7785                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7786             }
7787             
7788             if(hh.length){
7789                 c.html = hh;
7790             }
7791             
7792             if(typeof(config.tooltip) != 'undefined'){
7793                 c.tooltip = config.tooltip;
7794             }
7795             
7796             if(typeof(config.colspan) != 'undefined'){
7797                 c.colspan = config.colspan;
7798             }
7799             
7800             if(typeof(config.hidden) != 'undefined' && config.hidden){
7801                 c.style += ' display:none;';
7802             }
7803             
7804             if(typeof(config.dataIndex) != 'undefined'){
7805                 c.sort = config.dataIndex;
7806             }
7807             
7808            
7809             
7810             if(typeof(config.align) != 'undefined' && config.align.length){
7811                 c.style += ' text-align:' + config.align + ';';
7812             }
7813             
7814             if(typeof(config.width) != 'undefined'){
7815                 c.style += ' width:' + config.width + 'px;';
7816                 this.totalWidth += config.width;
7817             } else {
7818                 this.totalWidth += 100; // assume minimum of 100 per column?
7819             }
7820             
7821             if(typeof(config.cls) != 'undefined'){
7822                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7823             }
7824             
7825             ['xs','sm','md','lg'].map(function(size){
7826                 
7827                 if(typeof(config[size]) == 'undefined'){
7828                     return;
7829                 }
7830                  
7831                 if (!config[size]) { // 0 = hidden
7832                     // BS 4 '0' is treated as hide that column and below.
7833                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7834                     return;
7835                 }
7836                 
7837                 c.cls += ' col-' + size + '-' + config[size] + (
7838                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7839                 );
7840                 
7841                 
7842             });
7843             
7844             header.cn.push(c)
7845         }
7846         
7847         return header;
7848     },
7849     
7850     renderBody : function()
7851     {
7852         var body = {
7853             tag: 'tbody',
7854             cn : [
7855                 {
7856                     tag: 'tr',
7857                     cn : [
7858                         {
7859                             tag : 'td',
7860                             colspan :  this.cm.getColumnCount()
7861                         }
7862                     ]
7863                 }
7864             ]
7865         };
7866         
7867         return body;
7868     },
7869     
7870     renderFooter : function()
7871     {
7872         var footer = {
7873             tag: 'tfoot',
7874             cn : [
7875                 {
7876                     tag: 'tr',
7877                     cn : [
7878                         {
7879                             tag : 'td',
7880                             colspan :  this.cm.getColumnCount()
7881                         }
7882                     ]
7883                 }
7884             ]
7885         };
7886         
7887         return footer;
7888     },
7889     
7890     
7891     
7892     onLoad : function()
7893     {
7894 //        Roo.log('ds onload');
7895         this.clear();
7896         
7897         var _this = this;
7898         var cm = this.cm;
7899         var ds = this.store;
7900         
7901         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7902             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7903             if (_this.store.sortInfo) {
7904                     
7905                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7906                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7907                 }
7908                 
7909                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7910                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7911                 }
7912             }
7913         });
7914         
7915         var tbody =  this.mainBody;
7916               
7917         if(ds.getCount() > 0){
7918             ds.data.each(function(d,rowIndex){
7919                 var row =  this.renderRow(cm, ds, rowIndex);
7920                 
7921                 tbody.createChild(row);
7922                 
7923                 var _this = this;
7924                 
7925                 if(row.cellObjects.length){
7926                     Roo.each(row.cellObjects, function(r){
7927                         _this.renderCellObject(r);
7928                     })
7929                 }
7930                 
7931             }, this);
7932         }
7933         
7934         var tfoot = this.el.select('tfoot', true).first();
7935         
7936         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7937             
7938             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7939             
7940             var total = this.ds.getTotalCount();
7941             
7942             if(this.footer.pageSize < total){
7943                 this.mainFoot.show();
7944             }
7945         }
7946         
7947         Roo.each(this.el.select('tbody td', true).elements, function(e){
7948             e.on('mouseover', _this.onMouseover, _this);
7949         });
7950         
7951         Roo.each(this.el.select('tbody td', true).elements, function(e){
7952             e.on('mouseout', _this.onMouseout, _this);
7953         });
7954         this.fireEvent('rowsrendered', this);
7955         
7956         this.autoSize();
7957     },
7958     
7959     
7960     onUpdate : function(ds,record)
7961     {
7962         this.refreshRow(record);
7963         this.autoSize();
7964     },
7965     
7966     onRemove : function(ds, record, index, isUpdate){
7967         if(isUpdate !== true){
7968             this.fireEvent("beforerowremoved", this, index, record);
7969         }
7970         var bt = this.mainBody.dom;
7971         
7972         var rows = this.el.select('tbody > tr', true).elements;
7973         
7974         if(typeof(rows[index]) != 'undefined'){
7975             bt.removeChild(rows[index].dom);
7976         }
7977         
7978 //        if(bt.rows[index]){
7979 //            bt.removeChild(bt.rows[index]);
7980 //        }
7981         
7982         if(isUpdate !== true){
7983             //this.stripeRows(index);
7984             //this.syncRowHeights(index, index);
7985             //this.layout();
7986             this.fireEvent("rowremoved", this, index, record);
7987         }
7988     },
7989     
7990     onAdd : function(ds, records, rowIndex)
7991     {
7992         //Roo.log('on Add called');
7993         // - note this does not handle multiple adding very well..
7994         var bt = this.mainBody.dom;
7995         for (var i =0 ; i < records.length;i++) {
7996             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7997             //Roo.log(records[i]);
7998             //Roo.log(this.store.getAt(rowIndex+i));
7999             this.insertRow(this.store, rowIndex + i, false);
8000             return;
8001         }
8002         
8003     },
8004     
8005     
8006     refreshRow : function(record){
8007         var ds = this.store, index;
8008         if(typeof record == 'number'){
8009             index = record;
8010             record = ds.getAt(index);
8011         }else{
8012             index = ds.indexOf(record);
8013         }
8014         this.insertRow(ds, index, true);
8015         this.autoSize();
8016         this.onRemove(ds, record, index+1, true);
8017         this.autoSize();
8018         //this.syncRowHeights(index, index);
8019         //this.layout();
8020         this.fireEvent("rowupdated", this, index, record);
8021     },
8022     
8023     insertRow : function(dm, rowIndex, isUpdate){
8024         
8025         if(!isUpdate){
8026             this.fireEvent("beforerowsinserted", this, rowIndex);
8027         }
8028             //var s = this.getScrollState();
8029         var row = this.renderRow(this.cm, this.store, rowIndex);
8030         // insert before rowIndex..
8031         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8032         
8033         var _this = this;
8034                 
8035         if(row.cellObjects.length){
8036             Roo.each(row.cellObjects, function(r){
8037                 _this.renderCellObject(r);
8038             })
8039         }
8040             
8041         if(!isUpdate){
8042             this.fireEvent("rowsinserted", this, rowIndex);
8043             //this.syncRowHeights(firstRow, lastRow);
8044             //this.stripeRows(firstRow);
8045             //this.layout();
8046         }
8047         
8048     },
8049     
8050     
8051     getRowDom : function(rowIndex)
8052     {
8053         var rows = this.el.select('tbody > tr', true).elements;
8054         
8055         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8056         
8057     },
8058     // returns the object tree for a tr..
8059   
8060     
8061     renderRow : function(cm, ds, rowIndex) 
8062     {
8063         var d = ds.getAt(rowIndex);
8064         
8065         var row = {
8066             tag : 'tr',
8067             cls : 'x-row-' + rowIndex,
8068             cn : []
8069         };
8070             
8071         var cellObjects = [];
8072         
8073         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8074             var config = cm.config[i];
8075             
8076             var renderer = cm.getRenderer(i);
8077             var value = '';
8078             var id = false;
8079             
8080             if(typeof(renderer) !== 'undefined'){
8081                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8082             }
8083             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8084             // and are rendered into the cells after the row is rendered - using the id for the element.
8085             
8086             if(typeof(value) === 'object'){
8087                 id = Roo.id();
8088                 cellObjects.push({
8089                     container : id,
8090                     cfg : value 
8091                 })
8092             }
8093             
8094             var rowcfg = {
8095                 record: d,
8096                 rowIndex : rowIndex,
8097                 colIndex : i,
8098                 rowClass : ''
8099             };
8100
8101             this.fireEvent('rowclass', this, rowcfg);
8102             
8103             var td = {
8104                 tag: 'td',
8105                 cls : rowcfg.rowClass + ' x-col-' + i,
8106                 style: '',
8107                 html: (typeof(value) === 'object') ? '' : value
8108             };
8109             
8110             if (id) {
8111                 td.id = id;
8112             }
8113             
8114             if(typeof(config.colspan) != 'undefined'){
8115                 td.colspan = config.colspan;
8116             }
8117             
8118             if(typeof(config.hidden) != 'undefined' && config.hidden){
8119                 td.style += ' display:none;';
8120             }
8121             
8122             if(typeof(config.align) != 'undefined' && config.align.length){
8123                 td.style += ' text-align:' + config.align + ';';
8124             }
8125             if(typeof(config.valign) != 'undefined' && config.valign.length){
8126                 td.style += ' vertical-align:' + config.valign + ';';
8127             }
8128             
8129             if(typeof(config.width) != 'undefined'){
8130                 td.style += ' width:' +  config.width + 'px;';
8131             }
8132             
8133             if(typeof(config.cursor) != 'undefined'){
8134                 td.style += ' cursor:' +  config.cursor + ';';
8135             }
8136             
8137             if(typeof(config.cls) != 'undefined'){
8138                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8139             }
8140             
8141             ['xs','sm','md','lg'].map(function(size){
8142                 
8143                 if(typeof(config[size]) == 'undefined'){
8144                     return;
8145                 }
8146                 
8147                 
8148                   
8149                 if (!config[size]) { // 0 = hidden
8150                     // BS 4 '0' is treated as hide that column and below.
8151                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8152                     return;
8153                 }
8154                 
8155                 td.cls += ' col-' + size + '-' + config[size] + (
8156                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8157                 );
8158                  
8159
8160             });
8161             
8162             row.cn.push(td);
8163            
8164         }
8165         
8166         row.cellObjects = cellObjects;
8167         
8168         return row;
8169           
8170     },
8171     
8172     
8173     
8174     onBeforeLoad : function()
8175     {
8176         
8177     },
8178      /**
8179      * Remove all rows
8180      */
8181     clear : function()
8182     {
8183         this.el.select('tbody', true).first().dom.innerHTML = '';
8184     },
8185     /**
8186      * Show or hide a row.
8187      * @param {Number} rowIndex to show or hide
8188      * @param {Boolean} state hide
8189      */
8190     setRowVisibility : function(rowIndex, state)
8191     {
8192         var bt = this.mainBody.dom;
8193         
8194         var rows = this.el.select('tbody > tr', true).elements;
8195         
8196         if(typeof(rows[rowIndex]) == 'undefined'){
8197             return;
8198         }
8199         rows[rowIndex].dom.style.display = state ? '' : 'none';
8200     },
8201     
8202     
8203     getSelectionModel : function(){
8204         if(!this.selModel){
8205             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8206         }
8207         return this.selModel;
8208     },
8209     /*
8210      * Render the Roo.bootstrap object from renderder
8211      */
8212     renderCellObject : function(r)
8213     {
8214         var _this = this;
8215         
8216         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8217         
8218         var t = r.cfg.render(r.container);
8219         
8220         if(r.cfg.cn){
8221             Roo.each(r.cfg.cn, function(c){
8222                 var child = {
8223                     container: t.getChildContainer(),
8224                     cfg: c
8225                 };
8226                 _this.renderCellObject(child);
8227             })
8228         }
8229     },
8230     
8231     getRowIndex : function(row)
8232     {
8233         var rowIndex = -1;
8234         
8235         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8236             if(el != row){
8237                 return;
8238             }
8239             
8240             rowIndex = index;
8241         });
8242         
8243         return rowIndex;
8244     },
8245      /**
8246      * Returns the grid's underlying element = used by panel.Grid
8247      * @return {Element} The element
8248      */
8249     getGridEl : function(){
8250         return this.el;
8251     },
8252      /**
8253      * Forces a resize - used by panel.Grid
8254      * @return {Element} The element
8255      */
8256     autoSize : function()
8257     {
8258         //var ctr = Roo.get(this.container.dom.parentElement);
8259         var ctr = Roo.get(this.el.dom);
8260         
8261         var thd = this.getGridEl().select('thead',true).first();
8262         var tbd = this.getGridEl().select('tbody', true).first();
8263         var tfd = this.getGridEl().select('tfoot', true).first();
8264         
8265         var cw = ctr.getWidth();
8266         
8267         if (tbd) {
8268             
8269             tbd.setWidth(ctr.getWidth());
8270             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8271             // this needs fixing for various usage - currently only hydra job advers I think..
8272             //tdb.setHeight(
8273             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8274             //); 
8275             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8276             cw -= barsize;
8277         }
8278         cw = Math.max(cw, this.totalWidth);
8279         this.getGridEl().select('tr',true).setWidth(cw);
8280         // resize 'expandable coloumn?
8281         
8282         return; // we doe not have a view in this design..
8283         
8284     },
8285     onBodyScroll: function()
8286     {
8287         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8288         if(this.mainHead){
8289             this.mainHead.setStyle({
8290                 'position' : 'relative',
8291                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8292             });
8293         }
8294         
8295         if(this.lazyLoad){
8296             
8297             var scrollHeight = this.mainBody.dom.scrollHeight;
8298             
8299             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8300             
8301             var height = this.mainBody.getHeight();
8302             
8303             if(scrollHeight - height == scrollTop) {
8304                 
8305                 var total = this.ds.getTotalCount();
8306                 
8307                 if(this.footer.cursor + this.footer.pageSize < total){
8308                     
8309                     this.footer.ds.load({
8310                         params : {
8311                             start : this.footer.cursor + this.footer.pageSize,
8312                             limit : this.footer.pageSize
8313                         },
8314                         add : true
8315                     });
8316                 }
8317             }
8318             
8319         }
8320     },
8321     
8322     onHeaderChange : function()
8323     {
8324         var header = this.renderHeader();
8325         var table = this.el.select('table', true).first();
8326         
8327         this.mainHead.remove();
8328         this.mainHead = table.createChild(header, this.mainBody, false);
8329     },
8330     
8331     onHiddenChange : function(colModel, colIndex, hidden)
8332     {
8333         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8334         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8335         
8336         this.CSS.updateRule(thSelector, "display", "");
8337         this.CSS.updateRule(tdSelector, "display", "");
8338         
8339         if(hidden){
8340             this.CSS.updateRule(thSelector, "display", "none");
8341             this.CSS.updateRule(tdSelector, "display", "none");
8342         }
8343         
8344         this.onHeaderChange();
8345         this.onLoad();
8346     },
8347     
8348     setColumnWidth: function(col_index, width)
8349     {
8350         // width = "md-2 xs-2..."
8351         if(!this.colModel.config[col_index]) {
8352             return;
8353         }
8354         
8355         var w = width.split(" ");
8356         
8357         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8358         
8359         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8360         
8361         
8362         for(var j = 0; j < w.length; j++) {
8363             
8364             if(!w[j]) {
8365                 continue;
8366             }
8367             
8368             var size_cls = w[j].split("-");
8369             
8370             if(!Number.isInteger(size_cls[1] * 1)) {
8371                 continue;
8372             }
8373             
8374             if(!this.colModel.config[col_index][size_cls[0]]) {
8375                 continue;
8376             }
8377             
8378             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8379                 continue;
8380             }
8381             
8382             h_row[0].classList.replace(
8383                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8384                 "col-"+size_cls[0]+"-"+size_cls[1]
8385             );
8386             
8387             for(var i = 0; i < rows.length; i++) {
8388                 
8389                 var size_cls = w[j].split("-");
8390                 
8391                 if(!Number.isInteger(size_cls[1] * 1)) {
8392                     continue;
8393                 }
8394                 
8395                 if(!this.colModel.config[col_index][size_cls[0]]) {
8396                     continue;
8397                 }
8398                 
8399                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8400                     continue;
8401                 }
8402                 
8403                 rows[i].classList.replace(
8404                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8405                     "col-"+size_cls[0]+"-"+size_cls[1]
8406                 );
8407             }
8408             
8409             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8410         }
8411     }
8412 });
8413
8414  
8415
8416  /*
8417  * - LGPL
8418  *
8419  * table cell
8420  * 
8421  */
8422
8423 /**
8424  * @class Roo.bootstrap.TableCell
8425  * @extends Roo.bootstrap.Component
8426  * Bootstrap TableCell class
8427  * @cfg {String} html cell contain text
8428  * @cfg {String} cls cell class
8429  * @cfg {String} tag cell tag (td|th) default td
8430  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8431  * @cfg {String} align Aligns the content in a cell
8432  * @cfg {String} axis Categorizes cells
8433  * @cfg {String} bgcolor Specifies the background color of a cell
8434  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8435  * @cfg {Number} colspan Specifies the number of columns a cell should span
8436  * @cfg {String} headers Specifies one or more header cells a cell is related to
8437  * @cfg {Number} height Sets the height of a cell
8438  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8439  * @cfg {Number} rowspan Sets the number of rows a cell should span
8440  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8441  * @cfg {String} valign Vertical aligns the content in a cell
8442  * @cfg {Number} width Specifies the width of a cell
8443  * 
8444  * @constructor
8445  * Create a new TableCell
8446  * @param {Object} config The config object
8447  */
8448
8449 Roo.bootstrap.TableCell = function(config){
8450     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8451 };
8452
8453 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8454     
8455     html: false,
8456     cls: false,
8457     tag: false,
8458     abbr: false,
8459     align: false,
8460     axis: false,
8461     bgcolor: false,
8462     charoff: false,
8463     colspan: false,
8464     headers: false,
8465     height: false,
8466     nowrap: false,
8467     rowspan: false,
8468     scope: false,
8469     valign: false,
8470     width: false,
8471     
8472     
8473     getAutoCreate : function(){
8474         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8475         
8476         cfg = {
8477             tag: 'td'
8478         };
8479         
8480         if(this.tag){
8481             cfg.tag = this.tag;
8482         }
8483         
8484         if (this.html) {
8485             cfg.html=this.html
8486         }
8487         if (this.cls) {
8488             cfg.cls=this.cls
8489         }
8490         if (this.abbr) {
8491             cfg.abbr=this.abbr
8492         }
8493         if (this.align) {
8494             cfg.align=this.align
8495         }
8496         if (this.axis) {
8497             cfg.axis=this.axis
8498         }
8499         if (this.bgcolor) {
8500             cfg.bgcolor=this.bgcolor
8501         }
8502         if (this.charoff) {
8503             cfg.charoff=this.charoff
8504         }
8505         if (this.colspan) {
8506             cfg.colspan=this.colspan
8507         }
8508         if (this.headers) {
8509             cfg.headers=this.headers
8510         }
8511         if (this.height) {
8512             cfg.height=this.height
8513         }
8514         if (this.nowrap) {
8515             cfg.nowrap=this.nowrap
8516         }
8517         if (this.rowspan) {
8518             cfg.rowspan=this.rowspan
8519         }
8520         if (this.scope) {
8521             cfg.scope=this.scope
8522         }
8523         if (this.valign) {
8524             cfg.valign=this.valign
8525         }
8526         if (this.width) {
8527             cfg.width=this.width
8528         }
8529         
8530         
8531         return cfg;
8532     }
8533    
8534 });
8535
8536  
8537
8538  /*
8539  * - LGPL
8540  *
8541  * table row
8542  * 
8543  */
8544
8545 /**
8546  * @class Roo.bootstrap.TableRow
8547  * @extends Roo.bootstrap.Component
8548  * Bootstrap TableRow class
8549  * @cfg {String} cls row class
8550  * @cfg {String} align Aligns the content in a table row
8551  * @cfg {String} bgcolor Specifies a background color for a table row
8552  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8553  * @cfg {String} valign Vertical aligns the content in a table row
8554  * 
8555  * @constructor
8556  * Create a new TableRow
8557  * @param {Object} config The config object
8558  */
8559
8560 Roo.bootstrap.TableRow = function(config){
8561     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8562 };
8563
8564 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8565     
8566     cls: false,
8567     align: false,
8568     bgcolor: false,
8569     charoff: false,
8570     valign: false,
8571     
8572     getAutoCreate : function(){
8573         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8574         
8575         cfg = {
8576             tag: 'tr'
8577         };
8578             
8579         if(this.cls){
8580             cfg.cls = this.cls;
8581         }
8582         if(this.align){
8583             cfg.align = this.align;
8584         }
8585         if(this.bgcolor){
8586             cfg.bgcolor = this.bgcolor;
8587         }
8588         if(this.charoff){
8589             cfg.charoff = this.charoff;
8590         }
8591         if(this.valign){
8592             cfg.valign = this.valign;
8593         }
8594         
8595         return cfg;
8596     }
8597    
8598 });
8599
8600  
8601
8602  /*
8603  * - LGPL
8604  *
8605  * table body
8606  * 
8607  */
8608
8609 /**
8610  * @class Roo.bootstrap.TableBody
8611  * @extends Roo.bootstrap.Component
8612  * Bootstrap TableBody class
8613  * @cfg {String} cls element class
8614  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8615  * @cfg {String} align Aligns the content inside the element
8616  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8617  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8618  * 
8619  * @constructor
8620  * Create a new TableBody
8621  * @param {Object} config The config object
8622  */
8623
8624 Roo.bootstrap.TableBody = function(config){
8625     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8626 };
8627
8628 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8629     
8630     cls: false,
8631     tag: false,
8632     align: false,
8633     charoff: false,
8634     valign: false,
8635     
8636     getAutoCreate : function(){
8637         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8638         
8639         cfg = {
8640             tag: 'tbody'
8641         };
8642             
8643         if (this.cls) {
8644             cfg.cls=this.cls
8645         }
8646         if(this.tag){
8647             cfg.tag = this.tag;
8648         }
8649         
8650         if(this.align){
8651             cfg.align = this.align;
8652         }
8653         if(this.charoff){
8654             cfg.charoff = this.charoff;
8655         }
8656         if(this.valign){
8657             cfg.valign = this.valign;
8658         }
8659         
8660         return cfg;
8661     }
8662     
8663     
8664 //    initEvents : function()
8665 //    {
8666 //        
8667 //        if(!this.store){
8668 //            return;
8669 //        }
8670 //        
8671 //        this.store = Roo.factory(this.store, Roo.data);
8672 //        this.store.on('load', this.onLoad, this);
8673 //        
8674 //        this.store.load();
8675 //        
8676 //    },
8677 //    
8678 //    onLoad: function () 
8679 //    {   
8680 //        this.fireEvent('load', this);
8681 //    }
8682 //    
8683 //   
8684 });
8685
8686  
8687
8688  /*
8689  * Based on:
8690  * Ext JS Library 1.1.1
8691  * Copyright(c) 2006-2007, Ext JS, LLC.
8692  *
8693  * Originally Released Under LGPL - original licence link has changed is not relivant.
8694  *
8695  * Fork - LGPL
8696  * <script type="text/javascript">
8697  */
8698
8699 // as we use this in bootstrap.
8700 Roo.namespace('Roo.form');
8701  /**
8702  * @class Roo.form.Action
8703  * Internal Class used to handle form actions
8704  * @constructor
8705  * @param {Roo.form.BasicForm} el The form element or its id
8706  * @param {Object} config Configuration options
8707  */
8708
8709  
8710  
8711 // define the action interface
8712 Roo.form.Action = function(form, options){
8713     this.form = form;
8714     this.options = options || {};
8715 };
8716 /**
8717  * Client Validation Failed
8718  * @const 
8719  */
8720 Roo.form.Action.CLIENT_INVALID = 'client';
8721 /**
8722  * Server Validation Failed
8723  * @const 
8724  */
8725 Roo.form.Action.SERVER_INVALID = 'server';
8726  /**
8727  * Connect to Server Failed
8728  * @const 
8729  */
8730 Roo.form.Action.CONNECT_FAILURE = 'connect';
8731 /**
8732  * Reading Data from Server Failed
8733  * @const 
8734  */
8735 Roo.form.Action.LOAD_FAILURE = 'load';
8736
8737 Roo.form.Action.prototype = {
8738     type : 'default',
8739     failureType : undefined,
8740     response : undefined,
8741     result : undefined,
8742
8743     // interface method
8744     run : function(options){
8745
8746     },
8747
8748     // interface method
8749     success : function(response){
8750
8751     },
8752
8753     // interface method
8754     handleResponse : function(response){
8755
8756     },
8757
8758     // default connection failure
8759     failure : function(response){
8760         
8761         this.response = response;
8762         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8763         this.form.afterAction(this, false);
8764     },
8765
8766     processResponse : function(response){
8767         this.response = response;
8768         if(!response.responseText){
8769             return true;
8770         }
8771         this.result = this.handleResponse(response);
8772         return this.result;
8773     },
8774
8775     // utility functions used internally
8776     getUrl : function(appendParams){
8777         var url = this.options.url || this.form.url || this.form.el.dom.action;
8778         if(appendParams){
8779             var p = this.getParams();
8780             if(p){
8781                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8782             }
8783         }
8784         return url;
8785     },
8786
8787     getMethod : function(){
8788         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8789     },
8790
8791     getParams : function(){
8792         var bp = this.form.baseParams;
8793         var p = this.options.params;
8794         if(p){
8795             if(typeof p == "object"){
8796                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8797             }else if(typeof p == 'string' && bp){
8798                 p += '&' + Roo.urlEncode(bp);
8799             }
8800         }else if(bp){
8801             p = Roo.urlEncode(bp);
8802         }
8803         return p;
8804     },
8805
8806     createCallback : function(){
8807         return {
8808             success: this.success,
8809             failure: this.failure,
8810             scope: this,
8811             timeout: (this.form.timeout*1000),
8812             upload: this.form.fileUpload ? this.success : undefined
8813         };
8814     }
8815 };
8816
8817 Roo.form.Action.Submit = function(form, options){
8818     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8819 };
8820
8821 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8822     type : 'submit',
8823
8824     haveProgress : false,
8825     uploadComplete : false,
8826     
8827     // uploadProgress indicator.
8828     uploadProgress : function()
8829     {
8830         if (!this.form.progressUrl) {
8831             return;
8832         }
8833         
8834         if (!this.haveProgress) {
8835             Roo.MessageBox.progress("Uploading", "Uploading");
8836         }
8837         if (this.uploadComplete) {
8838            Roo.MessageBox.hide();
8839            return;
8840         }
8841         
8842         this.haveProgress = true;
8843    
8844         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8845         
8846         var c = new Roo.data.Connection();
8847         c.request({
8848             url : this.form.progressUrl,
8849             params: {
8850                 id : uid
8851             },
8852             method: 'GET',
8853             success : function(req){
8854                //console.log(data);
8855                 var rdata = false;
8856                 var edata;
8857                 try  {
8858                    rdata = Roo.decode(req.responseText)
8859                 } catch (e) {
8860                     Roo.log("Invalid data from server..");
8861                     Roo.log(edata);
8862                     return;
8863                 }
8864                 if (!rdata || !rdata.success) {
8865                     Roo.log(rdata);
8866                     Roo.MessageBox.alert(Roo.encode(rdata));
8867                     return;
8868                 }
8869                 var data = rdata.data;
8870                 
8871                 if (this.uploadComplete) {
8872                    Roo.MessageBox.hide();
8873                    return;
8874                 }
8875                    
8876                 if (data){
8877                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8878                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8879                     );
8880                 }
8881                 this.uploadProgress.defer(2000,this);
8882             },
8883        
8884             failure: function(data) {
8885                 Roo.log('progress url failed ');
8886                 Roo.log(data);
8887             },
8888             scope : this
8889         });
8890            
8891     },
8892     
8893     
8894     run : function()
8895     {
8896         // run get Values on the form, so it syncs any secondary forms.
8897         this.form.getValues();
8898         
8899         var o = this.options;
8900         var method = this.getMethod();
8901         var isPost = method == 'POST';
8902         if(o.clientValidation === false || this.form.isValid()){
8903             
8904             if (this.form.progressUrl) {
8905                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8906                     (new Date() * 1) + '' + Math.random());
8907                     
8908             } 
8909             
8910             
8911             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8912                 form:this.form.el.dom,
8913                 url:this.getUrl(!isPost),
8914                 method: method,
8915                 params:isPost ? this.getParams() : null,
8916                 isUpload: this.form.fileUpload,
8917                 formData : this.form.formData
8918             }));
8919             
8920             this.uploadProgress();
8921
8922         }else if (o.clientValidation !== false){ // client validation failed
8923             this.failureType = Roo.form.Action.CLIENT_INVALID;
8924             this.form.afterAction(this, false);
8925         }
8926     },
8927
8928     success : function(response)
8929     {
8930         this.uploadComplete= true;
8931         if (this.haveProgress) {
8932             Roo.MessageBox.hide();
8933         }
8934         
8935         
8936         var result = this.processResponse(response);
8937         if(result === true || result.success){
8938             this.form.afterAction(this, true);
8939             return;
8940         }
8941         if(result.errors){
8942             this.form.markInvalid(result.errors);
8943             this.failureType = Roo.form.Action.SERVER_INVALID;
8944         }
8945         this.form.afterAction(this, false);
8946     },
8947     failure : function(response)
8948     {
8949         this.uploadComplete= true;
8950         if (this.haveProgress) {
8951             Roo.MessageBox.hide();
8952         }
8953         
8954         this.response = response;
8955         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8956         this.form.afterAction(this, false);
8957     },
8958     
8959     handleResponse : function(response){
8960         if(this.form.errorReader){
8961             var rs = this.form.errorReader.read(response);
8962             var errors = [];
8963             if(rs.records){
8964                 for(var i = 0, len = rs.records.length; i < len; i++) {
8965                     var r = rs.records[i];
8966                     errors[i] = r.data;
8967                 }
8968             }
8969             if(errors.length < 1){
8970                 errors = null;
8971             }
8972             return {
8973                 success : rs.success,
8974                 errors : errors
8975             };
8976         }
8977         var ret = false;
8978         try {
8979             ret = Roo.decode(response.responseText);
8980         } catch (e) {
8981             ret = {
8982                 success: false,
8983                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8984                 errors : []
8985             };
8986         }
8987         return ret;
8988         
8989     }
8990 });
8991
8992
8993 Roo.form.Action.Load = function(form, options){
8994     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8995     this.reader = this.form.reader;
8996 };
8997
8998 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8999     type : 'load',
9000
9001     run : function(){
9002         
9003         Roo.Ajax.request(Roo.apply(
9004                 this.createCallback(), {
9005                     method:this.getMethod(),
9006                     url:this.getUrl(false),
9007                     params:this.getParams()
9008         }));
9009     },
9010
9011     success : function(response){
9012         
9013         var result = this.processResponse(response);
9014         if(result === true || !result.success || !result.data){
9015             this.failureType = Roo.form.Action.LOAD_FAILURE;
9016             this.form.afterAction(this, false);
9017             return;
9018         }
9019         this.form.clearInvalid();
9020         this.form.setValues(result.data);
9021         this.form.afterAction(this, true);
9022     },
9023
9024     handleResponse : function(response){
9025         if(this.form.reader){
9026             var rs = this.form.reader.read(response);
9027             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9028             return {
9029                 success : rs.success,
9030                 data : data
9031             };
9032         }
9033         return Roo.decode(response.responseText);
9034     }
9035 });
9036
9037 Roo.form.Action.ACTION_TYPES = {
9038     'load' : Roo.form.Action.Load,
9039     'submit' : Roo.form.Action.Submit
9040 };/*
9041  * - LGPL
9042  *
9043  * form
9044  *
9045  */
9046
9047 /**
9048  * @class Roo.bootstrap.Form
9049  * @extends Roo.bootstrap.Component
9050  * Bootstrap Form class
9051  * @cfg {String} method  GET | POST (default POST)
9052  * @cfg {String} labelAlign top | left (default top)
9053  * @cfg {String} align left  | right - for navbars
9054  * @cfg {Boolean} loadMask load mask when submit (default true)
9055
9056  *
9057  * @constructor
9058  * Create a new Form
9059  * @param {Object} config The config object
9060  */
9061
9062
9063 Roo.bootstrap.Form = function(config){
9064     
9065     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9066     
9067     Roo.bootstrap.Form.popover.apply();
9068     
9069     this.addEvents({
9070         /**
9071          * @event clientvalidation
9072          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9073          * @param {Form} this
9074          * @param {Boolean} valid true if the form has passed client-side validation
9075          */
9076         clientvalidation: true,
9077         /**
9078          * @event beforeaction
9079          * Fires before any action is performed. Return false to cancel the action.
9080          * @param {Form} this
9081          * @param {Action} action The action to be performed
9082          */
9083         beforeaction: true,
9084         /**
9085          * @event actionfailed
9086          * Fires when an action fails.
9087          * @param {Form} this
9088          * @param {Action} action The action that failed
9089          */
9090         actionfailed : true,
9091         /**
9092          * @event actioncomplete
9093          * Fires when an action is completed.
9094          * @param {Form} this
9095          * @param {Action} action The action that completed
9096          */
9097         actioncomplete : true
9098     });
9099 };
9100
9101 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9102
9103      /**
9104      * @cfg {String} method
9105      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9106      */
9107     method : 'POST',
9108     /**
9109      * @cfg {String} url
9110      * The URL to use for form actions if one isn't supplied in the action options.
9111      */
9112     /**
9113      * @cfg {Boolean} fileUpload
9114      * Set to true if this form is a file upload.
9115      */
9116
9117     /**
9118      * @cfg {Object} baseParams
9119      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9120      */
9121
9122     /**
9123      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9124      */
9125     timeout: 30,
9126     /**
9127      * @cfg {Sting} align (left|right) for navbar forms
9128      */
9129     align : 'left',
9130
9131     // private
9132     activeAction : null,
9133
9134     /**
9135      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9136      * element by passing it or its id or mask the form itself by passing in true.
9137      * @type Mixed
9138      */
9139     waitMsgTarget : false,
9140
9141     loadMask : true,
9142     
9143     /**
9144      * @cfg {Boolean} errorMask (true|false) default false
9145      */
9146     errorMask : false,
9147     
9148     /**
9149      * @cfg {Number} maskOffset Default 100
9150      */
9151     maskOffset : 100,
9152     
9153     /**
9154      * @cfg {Boolean} maskBody
9155      */
9156     maskBody : false,
9157
9158     getAutoCreate : function(){
9159
9160         var cfg = {
9161             tag: 'form',
9162             method : this.method || 'POST',
9163             id : this.id || Roo.id(),
9164             cls : ''
9165         };
9166         if (this.parent().xtype.match(/^Nav/)) {
9167             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9168
9169         }
9170
9171         if (this.labelAlign == 'left' ) {
9172             cfg.cls += ' form-horizontal';
9173         }
9174
9175
9176         return cfg;
9177     },
9178     initEvents : function()
9179     {
9180         this.el.on('submit', this.onSubmit, this);
9181         // this was added as random key presses on the form where triggering form submit.
9182         this.el.on('keypress', function(e) {
9183             if (e.getCharCode() != 13) {
9184                 return true;
9185             }
9186             // we might need to allow it for textareas.. and some other items.
9187             // check e.getTarget().
9188
9189             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9190                 return true;
9191             }
9192
9193             Roo.log("keypress blocked");
9194
9195             e.preventDefault();
9196             return false;
9197         });
9198         
9199     },
9200     // private
9201     onSubmit : function(e){
9202         e.stopEvent();
9203     },
9204
9205      /**
9206      * Returns true if client-side validation on the form is successful.
9207      * @return Boolean
9208      */
9209     isValid : function(){
9210         var items = this.getItems();
9211         var valid = true;
9212         var target = false;
9213         
9214         items.each(function(f){
9215             
9216             if(f.validate()){
9217                 return;
9218             }
9219             
9220             Roo.log('invalid field: ' + f.name);
9221             
9222             valid = false;
9223
9224             if(!target && f.el.isVisible(true)){
9225                 target = f;
9226             }
9227            
9228         });
9229         
9230         if(this.errorMask && !valid){
9231             Roo.bootstrap.Form.popover.mask(this, target);
9232         }
9233         
9234         return valid;
9235     },
9236     
9237     /**
9238      * Returns true if any fields in this form have changed since their original load.
9239      * @return Boolean
9240      */
9241     isDirty : function(){
9242         var dirty = false;
9243         var items = this.getItems();
9244         items.each(function(f){
9245            if(f.isDirty()){
9246                dirty = true;
9247                return false;
9248            }
9249            return true;
9250         });
9251         return dirty;
9252     },
9253      /**
9254      * Performs a predefined action (submit or load) or custom actions you define on this form.
9255      * @param {String} actionName The name of the action type
9256      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9257      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9258      * accept other config options):
9259      * <pre>
9260 Property          Type             Description
9261 ----------------  ---------------  ----------------------------------------------------------------------------------
9262 url               String           The url for the action (defaults to the form's url)
9263 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9264 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9265 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9266                                    validate the form on the client (defaults to false)
9267      * </pre>
9268      * @return {BasicForm} this
9269      */
9270     doAction : function(action, options){
9271         if(typeof action == 'string'){
9272             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9273         }
9274         if(this.fireEvent('beforeaction', this, action) !== false){
9275             this.beforeAction(action);
9276             action.run.defer(100, action);
9277         }
9278         return this;
9279     },
9280
9281     // private
9282     beforeAction : function(action){
9283         var o = action.options;
9284         
9285         if(this.loadMask){
9286             
9287             if(this.maskBody){
9288                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9289             } else {
9290                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9291             }
9292         }
9293         // not really supported yet.. ??
9294
9295         //if(this.waitMsgTarget === true){
9296         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9297         //}else if(this.waitMsgTarget){
9298         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9299         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9300         //}else {
9301         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9302        // }
9303
9304     },
9305
9306     // private
9307     afterAction : function(action, success){
9308         this.activeAction = null;
9309         var o = action.options;
9310
9311         if(this.loadMask){
9312             
9313             if(this.maskBody){
9314                 Roo.get(document.body).unmask();
9315             } else {
9316                 this.el.unmask();
9317             }
9318         }
9319         
9320         //if(this.waitMsgTarget === true){
9321 //            this.el.unmask();
9322         //}else if(this.waitMsgTarget){
9323         //    this.waitMsgTarget.unmask();
9324         //}else{
9325         //    Roo.MessageBox.updateProgress(1);
9326         //    Roo.MessageBox.hide();
9327        // }
9328         //
9329         if(success){
9330             if(o.reset){
9331                 this.reset();
9332             }
9333             Roo.callback(o.success, o.scope, [this, action]);
9334             this.fireEvent('actioncomplete', this, action);
9335
9336         }else{
9337
9338             // failure condition..
9339             // we have a scenario where updates need confirming.
9340             // eg. if a locking scenario exists..
9341             // we look for { errors : { needs_confirm : true }} in the response.
9342             if (
9343                 (typeof(action.result) != 'undefined')  &&
9344                 (typeof(action.result.errors) != 'undefined')  &&
9345                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9346            ){
9347                 var _t = this;
9348                 Roo.log("not supported yet");
9349                  /*
9350
9351                 Roo.MessageBox.confirm(
9352                     "Change requires confirmation",
9353                     action.result.errorMsg,
9354                     function(r) {
9355                         if (r != 'yes') {
9356                             return;
9357                         }
9358                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9359                     }
9360
9361                 );
9362                 */
9363
9364
9365                 return;
9366             }
9367
9368             Roo.callback(o.failure, o.scope, [this, action]);
9369             // show an error message if no failed handler is set..
9370             if (!this.hasListener('actionfailed')) {
9371                 Roo.log("need to add dialog support");
9372                 /*
9373                 Roo.MessageBox.alert("Error",
9374                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9375                         action.result.errorMsg :
9376                         "Saving Failed, please check your entries or try again"
9377                 );
9378                 */
9379             }
9380
9381             this.fireEvent('actionfailed', this, action);
9382         }
9383
9384     },
9385     /**
9386      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9387      * @param {String} id The value to search for
9388      * @return Field
9389      */
9390     findField : function(id){
9391         var items = this.getItems();
9392         var field = items.get(id);
9393         if(!field){
9394              items.each(function(f){
9395                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9396                     field = f;
9397                     return false;
9398                 }
9399                 return true;
9400             });
9401         }
9402         return field || null;
9403     },
9404      /**
9405      * Mark fields in this form invalid in bulk.
9406      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9407      * @return {BasicForm} this
9408      */
9409     markInvalid : function(errors){
9410         if(errors instanceof Array){
9411             for(var i = 0, len = errors.length; i < len; i++){
9412                 var fieldError = errors[i];
9413                 var f = this.findField(fieldError.id);
9414                 if(f){
9415                     f.markInvalid(fieldError.msg);
9416                 }
9417             }
9418         }else{
9419             var field, id;
9420             for(id in errors){
9421                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9422                     field.markInvalid(errors[id]);
9423                 }
9424             }
9425         }
9426         //Roo.each(this.childForms || [], function (f) {
9427         //    f.markInvalid(errors);
9428         //});
9429
9430         return this;
9431     },
9432
9433     /**
9434      * Set values for fields in this form in bulk.
9435      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9436      * @return {BasicForm} this
9437      */
9438     setValues : function(values){
9439         if(values instanceof Array){ // array of objects
9440             for(var i = 0, len = values.length; i < len; i++){
9441                 var v = values[i];
9442                 var f = this.findField(v.id);
9443                 if(f){
9444                     f.setValue(v.value);
9445                     if(this.trackResetOnLoad){
9446                         f.originalValue = f.getValue();
9447                     }
9448                 }
9449             }
9450         }else{ // object hash
9451             var field, id;
9452             for(id in values){
9453                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9454
9455                     if (field.setFromData &&
9456                         field.valueField &&
9457                         field.displayField &&
9458                         // combos' with local stores can
9459                         // be queried via setValue()
9460                         // to set their value..
9461                         (field.store && !field.store.isLocal)
9462                         ) {
9463                         // it's a combo
9464                         var sd = { };
9465                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9466                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9467                         field.setFromData(sd);
9468
9469                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9470                         
9471                         field.setFromData(values);
9472                         
9473                     } else {
9474                         field.setValue(values[id]);
9475                     }
9476
9477
9478                     if(this.trackResetOnLoad){
9479                         field.originalValue = field.getValue();
9480                     }
9481                 }
9482             }
9483         }
9484
9485         //Roo.each(this.childForms || [], function (f) {
9486         //    f.setValues(values);
9487         //});
9488
9489         return this;
9490     },
9491
9492     /**
9493      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9494      * they are returned as an array.
9495      * @param {Boolean} asString
9496      * @return {Object}
9497      */
9498     getValues : function(asString){
9499         //if (this.childForms) {
9500             // copy values from the child forms
9501         //    Roo.each(this.childForms, function (f) {
9502         //        this.setValues(f.getValues());
9503         //    }, this);
9504         //}
9505
9506
9507
9508         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9509         if(asString === true){
9510             return fs;
9511         }
9512         return Roo.urlDecode(fs);
9513     },
9514
9515     /**
9516      * Returns the fields in this form as an object with key/value pairs.
9517      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9518      * @return {Object}
9519      */
9520     getFieldValues : function(with_hidden)
9521     {
9522         var items = this.getItems();
9523         var ret = {};
9524         items.each(function(f){
9525             
9526             if (!f.getName()) {
9527                 return;
9528             }
9529             
9530             var v = f.getValue();
9531             
9532             if (f.inputType =='radio') {
9533                 if (typeof(ret[f.getName()]) == 'undefined') {
9534                     ret[f.getName()] = ''; // empty..
9535                 }
9536
9537                 if (!f.el.dom.checked) {
9538                     return;
9539
9540                 }
9541                 v = f.el.dom.value;
9542
9543             }
9544             
9545             if(f.xtype == 'MoneyField'){
9546                 ret[f.currencyName] = f.getCurrency();
9547             }
9548
9549             // not sure if this supported any more..
9550             if ((typeof(v) == 'object') && f.getRawValue) {
9551                 v = f.getRawValue() ; // dates..
9552             }
9553             // combo boxes where name != hiddenName...
9554             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9555                 ret[f.name] = f.getRawValue();
9556             }
9557             ret[f.getName()] = v;
9558         });
9559
9560         return ret;
9561     },
9562
9563     /**
9564      * Clears all invalid messages in this form.
9565      * @return {BasicForm} this
9566      */
9567     clearInvalid : function(){
9568         var items = this.getItems();
9569
9570         items.each(function(f){
9571            f.clearInvalid();
9572         });
9573
9574         return this;
9575     },
9576
9577     /**
9578      * Resets this form.
9579      * @return {BasicForm} this
9580      */
9581     reset : function(){
9582         var items = this.getItems();
9583         items.each(function(f){
9584             f.reset();
9585         });
9586
9587         Roo.each(this.childForms || [], function (f) {
9588             f.reset();
9589         });
9590
9591
9592         return this;
9593     },
9594     
9595     getItems : function()
9596     {
9597         var r=new Roo.util.MixedCollection(false, function(o){
9598             return o.id || (o.id = Roo.id());
9599         });
9600         var iter = function(el) {
9601             if (el.inputEl) {
9602                 r.add(el);
9603             }
9604             if (!el.items) {
9605                 return;
9606             }
9607             Roo.each(el.items,function(e) {
9608                 iter(e);
9609             });
9610         };
9611
9612         iter(this);
9613         return r;
9614     },
9615     
9616     hideFields : function(items)
9617     {
9618         Roo.each(items, function(i){
9619             
9620             var f = this.findField(i);
9621             
9622             if(!f){
9623                 return;
9624             }
9625             
9626             f.hide();
9627             
9628         }, this);
9629     },
9630     
9631     showFields : function(items)
9632     {
9633         Roo.each(items, function(i){
9634             
9635             var f = this.findField(i);
9636             
9637             if(!f){
9638                 return;
9639             }
9640             
9641             f.show();
9642             
9643         }, this);
9644     }
9645
9646 });
9647
9648 Roo.apply(Roo.bootstrap.Form, {
9649     
9650     popover : {
9651         
9652         padding : 5,
9653         
9654         isApplied : false,
9655         
9656         isMasked : false,
9657         
9658         form : false,
9659         
9660         target : false,
9661         
9662         toolTip : false,
9663         
9664         intervalID : false,
9665         
9666         maskEl : false,
9667         
9668         apply : function()
9669         {
9670             if(this.isApplied){
9671                 return;
9672             }
9673             
9674             this.maskEl = {
9675                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9676                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9677                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9678                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9679             };
9680             
9681             this.maskEl.top.enableDisplayMode("block");
9682             this.maskEl.left.enableDisplayMode("block");
9683             this.maskEl.bottom.enableDisplayMode("block");
9684             this.maskEl.right.enableDisplayMode("block");
9685             
9686             this.toolTip = new Roo.bootstrap.Tooltip({
9687                 cls : 'roo-form-error-popover',
9688                 alignment : {
9689                     'left' : ['r-l', [-2,0], 'right'],
9690                     'right' : ['l-r', [2,0], 'left'],
9691                     'bottom' : ['tl-bl', [0,2], 'top'],
9692                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9693                 }
9694             });
9695             
9696             this.toolTip.render(Roo.get(document.body));
9697
9698             this.toolTip.el.enableDisplayMode("block");
9699             
9700             Roo.get(document.body).on('click', function(){
9701                 this.unmask();
9702             }, this);
9703             
9704             Roo.get(document.body).on('touchstart', function(){
9705                 this.unmask();
9706             }, this);
9707             
9708             this.isApplied = true
9709         },
9710         
9711         mask : function(form, target)
9712         {
9713             this.form = form;
9714             
9715             this.target = target;
9716             
9717             if(!this.form.errorMask || !target.el){
9718                 return;
9719             }
9720             
9721             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9722             
9723             Roo.log(scrollable);
9724             
9725             var ot = this.target.el.calcOffsetsTo(scrollable);
9726             
9727             var scrollTo = ot[1] - this.form.maskOffset;
9728             
9729             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9730             
9731             scrollable.scrollTo('top', scrollTo);
9732             
9733             var box = this.target.el.getBox();
9734             Roo.log(box);
9735             var zIndex = Roo.bootstrap.Modal.zIndex++;
9736
9737             
9738             this.maskEl.top.setStyle('position', 'absolute');
9739             this.maskEl.top.setStyle('z-index', zIndex);
9740             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9741             this.maskEl.top.setLeft(0);
9742             this.maskEl.top.setTop(0);
9743             this.maskEl.top.show();
9744             
9745             this.maskEl.left.setStyle('position', 'absolute');
9746             this.maskEl.left.setStyle('z-index', zIndex);
9747             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9748             this.maskEl.left.setLeft(0);
9749             this.maskEl.left.setTop(box.y - this.padding);
9750             this.maskEl.left.show();
9751
9752             this.maskEl.bottom.setStyle('position', 'absolute');
9753             this.maskEl.bottom.setStyle('z-index', zIndex);
9754             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9755             this.maskEl.bottom.setLeft(0);
9756             this.maskEl.bottom.setTop(box.bottom + this.padding);
9757             this.maskEl.bottom.show();
9758
9759             this.maskEl.right.setStyle('position', 'absolute');
9760             this.maskEl.right.setStyle('z-index', zIndex);
9761             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9762             this.maskEl.right.setLeft(box.right + this.padding);
9763             this.maskEl.right.setTop(box.y - this.padding);
9764             this.maskEl.right.show();
9765
9766             this.toolTip.bindEl = this.target.el;
9767
9768             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9769
9770             var tip = this.target.blankText;
9771
9772             if(this.target.getValue() !== '' ) {
9773                 
9774                 if (this.target.invalidText.length) {
9775                     tip = this.target.invalidText;
9776                 } else if (this.target.regexText.length){
9777                     tip = this.target.regexText;
9778                 }
9779             }
9780
9781             this.toolTip.show(tip);
9782
9783             this.intervalID = window.setInterval(function() {
9784                 Roo.bootstrap.Form.popover.unmask();
9785             }, 10000);
9786
9787             window.onwheel = function(){ return false;};
9788             
9789             (function(){ this.isMasked = true; }).defer(500, this);
9790             
9791         },
9792         
9793         unmask : function()
9794         {
9795             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9796                 return;
9797             }
9798             
9799             this.maskEl.top.setStyle('position', 'absolute');
9800             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9801             this.maskEl.top.hide();
9802
9803             this.maskEl.left.setStyle('position', 'absolute');
9804             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9805             this.maskEl.left.hide();
9806
9807             this.maskEl.bottom.setStyle('position', 'absolute');
9808             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9809             this.maskEl.bottom.hide();
9810
9811             this.maskEl.right.setStyle('position', 'absolute');
9812             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9813             this.maskEl.right.hide();
9814             
9815             this.toolTip.hide();
9816             
9817             this.toolTip.el.hide();
9818             
9819             window.onwheel = function(){ return true;};
9820             
9821             if(this.intervalID){
9822                 window.clearInterval(this.intervalID);
9823                 this.intervalID = false;
9824             }
9825             
9826             this.isMasked = false;
9827             
9828         }
9829         
9830     }
9831     
9832 });
9833
9834 /*
9835  * Based on:
9836  * Ext JS Library 1.1.1
9837  * Copyright(c) 2006-2007, Ext JS, LLC.
9838  *
9839  * Originally Released Under LGPL - original licence link has changed is not relivant.
9840  *
9841  * Fork - LGPL
9842  * <script type="text/javascript">
9843  */
9844 /**
9845  * @class Roo.form.VTypes
9846  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9847  * @singleton
9848  */
9849 Roo.form.VTypes = function(){
9850     // closure these in so they are only created once.
9851     var alpha = /^[a-zA-Z_]+$/;
9852     var alphanum = /^[a-zA-Z0-9_]+$/;
9853     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9854     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9855
9856     // All these messages and functions are configurable
9857     return {
9858         /**
9859          * The function used to validate email addresses
9860          * @param {String} value The email address
9861          */
9862         'email' : function(v){
9863             return email.test(v);
9864         },
9865         /**
9866          * The error text to display when the email validation function returns false
9867          * @type String
9868          */
9869         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9870         /**
9871          * The keystroke filter mask to be applied on email input
9872          * @type RegExp
9873          */
9874         'emailMask' : /[a-z0-9_\.\-@]/i,
9875
9876         /**
9877          * The function used to validate URLs
9878          * @param {String} value The URL
9879          */
9880         'url' : function(v){
9881             return url.test(v);
9882         },
9883         /**
9884          * The error text to display when the url validation function returns false
9885          * @type String
9886          */
9887         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9888         
9889         /**
9890          * The function used to validate alpha values
9891          * @param {String} value The value
9892          */
9893         'alpha' : function(v){
9894             return alpha.test(v);
9895         },
9896         /**
9897          * The error text to display when the alpha validation function returns false
9898          * @type String
9899          */
9900         'alphaText' : 'This field should only contain letters and _',
9901         /**
9902          * The keystroke filter mask to be applied on alpha input
9903          * @type RegExp
9904          */
9905         'alphaMask' : /[a-z_]/i,
9906
9907         /**
9908          * The function used to validate alphanumeric values
9909          * @param {String} value The value
9910          */
9911         'alphanum' : function(v){
9912             return alphanum.test(v);
9913         },
9914         /**
9915          * The error text to display when the alphanumeric validation function returns false
9916          * @type String
9917          */
9918         'alphanumText' : 'This field should only contain letters, numbers and _',
9919         /**
9920          * The keystroke filter mask to be applied on alphanumeric input
9921          * @type RegExp
9922          */
9923         'alphanumMask' : /[a-z0-9_]/i
9924     };
9925 }();/*
9926  * - LGPL
9927  *
9928  * Input
9929  * 
9930  */
9931
9932 /**
9933  * @class Roo.bootstrap.Input
9934  * @extends Roo.bootstrap.Component
9935  * Bootstrap Input class
9936  * @cfg {Boolean} disabled is it disabled
9937  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9938  * @cfg {String} name name of the input
9939  * @cfg {string} fieldLabel - the label associated
9940  * @cfg {string} placeholder - placeholder to put in text.
9941  * @cfg {string}  before - input group add on before
9942  * @cfg {string} after - input group add on after
9943  * @cfg {string} size - (lg|sm) or leave empty..
9944  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9945  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9946  * @cfg {Number} md colspan out of 12 for computer-sized screens
9947  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9948  * @cfg {string} value default value of the input
9949  * @cfg {Number} labelWidth set the width of label 
9950  * @cfg {Number} labellg set the width of label (1-12)
9951  * @cfg {Number} labelmd set the width of label (1-12)
9952  * @cfg {Number} labelsm set the width of label (1-12)
9953  * @cfg {Number} labelxs set the width of label (1-12)
9954  * @cfg {String} labelAlign (top|left)
9955  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9956  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9957  * @cfg {String} indicatorpos (left|right) default left
9958  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9959  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9960
9961  * @cfg {String} align (left|center|right) Default left
9962  * @cfg {Boolean} forceFeedback (true|false) Default false
9963  * 
9964  * @constructor
9965  * Create a new Input
9966  * @param {Object} config The config object
9967  */
9968
9969 Roo.bootstrap.Input = function(config){
9970     
9971     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9972     
9973     this.addEvents({
9974         /**
9975          * @event focus
9976          * Fires when this field receives input focus.
9977          * @param {Roo.form.Field} this
9978          */
9979         focus : true,
9980         /**
9981          * @event blur
9982          * Fires when this field loses input focus.
9983          * @param {Roo.form.Field} this
9984          */
9985         blur : true,
9986         /**
9987          * @event specialkey
9988          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9989          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9990          * @param {Roo.form.Field} this
9991          * @param {Roo.EventObject} e The event object
9992          */
9993         specialkey : true,
9994         /**
9995          * @event change
9996          * Fires just before the field blurs if the field value has changed.
9997          * @param {Roo.form.Field} this
9998          * @param {Mixed} newValue The new value
9999          * @param {Mixed} oldValue The original value
10000          */
10001         change : true,
10002         /**
10003          * @event invalid
10004          * Fires after the field has been marked as invalid.
10005          * @param {Roo.form.Field} this
10006          * @param {String} msg The validation message
10007          */
10008         invalid : true,
10009         /**
10010          * @event valid
10011          * Fires after the field has been validated with no errors.
10012          * @param {Roo.form.Field} this
10013          */
10014         valid : true,
10015          /**
10016          * @event keyup
10017          * Fires after the key up
10018          * @param {Roo.form.Field} this
10019          * @param {Roo.EventObject}  e The event Object
10020          */
10021         keyup : true
10022     });
10023 };
10024
10025 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10026      /**
10027      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10028       automatic validation (defaults to "keyup").
10029      */
10030     validationEvent : "keyup",
10031      /**
10032      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10033      */
10034     validateOnBlur : true,
10035     /**
10036      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10037      */
10038     validationDelay : 250,
10039      /**
10040      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10041      */
10042     focusClass : "x-form-focus",  // not needed???
10043     
10044        
10045     /**
10046      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10047      */
10048     invalidClass : "has-warning",
10049     
10050     /**
10051      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10052      */
10053     validClass : "has-success",
10054     
10055     /**
10056      * @cfg {Boolean} hasFeedback (true|false) default true
10057      */
10058     hasFeedback : true,
10059     
10060     /**
10061      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10062      */
10063     invalidFeedbackClass : "glyphicon-warning-sign",
10064     
10065     /**
10066      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10067      */
10068     validFeedbackClass : "glyphicon-ok",
10069     
10070     /**
10071      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10072      */
10073     selectOnFocus : false,
10074     
10075      /**
10076      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10077      */
10078     maskRe : null,
10079        /**
10080      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10081      */
10082     vtype : null,
10083     
10084       /**
10085      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10086      */
10087     disableKeyFilter : false,
10088     
10089        /**
10090      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10091      */
10092     disabled : false,
10093      /**
10094      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10095      */
10096     allowBlank : true,
10097     /**
10098      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10099      */
10100     blankText : "Please complete this mandatory field",
10101     
10102      /**
10103      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10104      */
10105     minLength : 0,
10106     /**
10107      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10108      */
10109     maxLength : Number.MAX_VALUE,
10110     /**
10111      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10112      */
10113     minLengthText : "The minimum length for this field is {0}",
10114     /**
10115      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10116      */
10117     maxLengthText : "The maximum length for this field is {0}",
10118   
10119     
10120     /**
10121      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10122      * If available, this function will be called only after the basic validators all return true, and will be passed the
10123      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10124      */
10125     validator : null,
10126     /**
10127      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10128      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10129      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10130      */
10131     regex : null,
10132     /**
10133      * @cfg {String} regexText -- Depricated - use Invalid Text
10134      */
10135     regexText : "",
10136     
10137     /**
10138      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10139      */
10140     invalidText : "",
10141     
10142     
10143     
10144     autocomplete: false,
10145     
10146     
10147     fieldLabel : '',
10148     inputType : 'text',
10149     
10150     name : false,
10151     placeholder: false,
10152     before : false,
10153     after : false,
10154     size : false,
10155     hasFocus : false,
10156     preventMark: false,
10157     isFormField : true,
10158     value : '',
10159     labelWidth : 2,
10160     labelAlign : false,
10161     readOnly : false,
10162     align : false,
10163     formatedValue : false,
10164     forceFeedback : false,
10165     
10166     indicatorpos : 'left',
10167     
10168     labellg : 0,
10169     labelmd : 0,
10170     labelsm : 0,
10171     labelxs : 0,
10172     
10173     capture : '',
10174     accept : '',
10175     
10176     parentLabelAlign : function()
10177     {
10178         var parent = this;
10179         while (parent.parent()) {
10180             parent = parent.parent();
10181             if (typeof(parent.labelAlign) !='undefined') {
10182                 return parent.labelAlign;
10183             }
10184         }
10185         return 'left';
10186         
10187     },
10188     
10189     getAutoCreate : function()
10190     {
10191         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10192         
10193         var id = Roo.id();
10194         
10195         var cfg = {};
10196         
10197         if(this.inputType != 'hidden'){
10198             cfg.cls = 'form-group' //input-group
10199         }
10200         
10201         var input =  {
10202             tag: 'input',
10203             id : id,
10204             type : this.inputType,
10205             value : this.value,
10206             cls : 'form-control',
10207             placeholder : this.placeholder || '',
10208             autocomplete : this.autocomplete || 'new-password'
10209         };
10210         
10211         if(this.capture.length){
10212             input.capture = this.capture;
10213         }
10214         
10215         if(this.accept.length){
10216             input.accept = this.accept + "/*";
10217         }
10218         
10219         if(this.align){
10220             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10221         }
10222         
10223         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10224             input.maxLength = this.maxLength;
10225         }
10226         
10227         if (this.disabled) {
10228             input.disabled=true;
10229         }
10230         
10231         if (this.readOnly) {
10232             input.readonly=true;
10233         }
10234         
10235         if (this.name) {
10236             input.name = this.name;
10237         }
10238         
10239         if (this.size) {
10240             input.cls += ' input-' + this.size;
10241         }
10242         
10243         var settings=this;
10244         ['xs','sm','md','lg'].map(function(size){
10245             if (settings[size]) {
10246                 cfg.cls += ' col-' + size + '-' + settings[size];
10247             }
10248         });
10249         
10250         var inputblock = input;
10251         
10252         var feedback = {
10253             tag: 'span',
10254             cls: 'glyphicon form-control-feedback'
10255         };
10256             
10257         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10258             
10259             inputblock = {
10260                 cls : 'has-feedback',
10261                 cn :  [
10262                     input,
10263                     feedback
10264                 ] 
10265             };  
10266         }
10267         
10268         if (this.before || this.after) {
10269             
10270             inputblock = {
10271                 cls : 'input-group',
10272                 cn :  [] 
10273             };
10274             
10275             if (this.before && typeof(this.before) == 'string') {
10276                 
10277                 inputblock.cn.push({
10278                     tag :'span',
10279                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10280                     html : this.before
10281                 });
10282             }
10283             if (this.before && typeof(this.before) == 'object') {
10284                 this.before = Roo.factory(this.before);
10285                 
10286                 inputblock.cn.push({
10287                     tag :'span',
10288                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10289                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10290                 });
10291             }
10292             
10293             inputblock.cn.push(input);
10294             
10295             if (this.after && typeof(this.after) == 'string') {
10296                 inputblock.cn.push({
10297                     tag :'span',
10298                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10299                     html : this.after
10300                 });
10301             }
10302             if (this.after && typeof(this.after) == 'object') {
10303                 this.after = Roo.factory(this.after);
10304                 
10305                 inputblock.cn.push({
10306                     tag :'span',
10307                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10308                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10309                 });
10310             }
10311             
10312             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10313                 inputblock.cls += ' has-feedback';
10314                 inputblock.cn.push(feedback);
10315             }
10316         };
10317         var indicator = {
10318             tag : 'i',
10319             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10320             tooltip : 'This field is required'
10321         };
10322         if (Roo.bootstrap.version == 4) {
10323             indicator = {
10324                 tag : 'i',
10325                 style : 'display-none'
10326             };
10327         }
10328         if (align ==='left' && this.fieldLabel.length) {
10329             
10330             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10331             
10332             cfg.cn = [
10333                 indicator,
10334                 {
10335                     tag: 'label',
10336                     'for' :  id,
10337                     cls : 'control-label col-form-label',
10338                     html : this.fieldLabel
10339
10340                 },
10341                 {
10342                     cls : "", 
10343                     cn: [
10344                         inputblock
10345                     ]
10346                 }
10347             ];
10348             
10349             var labelCfg = cfg.cn[1];
10350             var contentCfg = cfg.cn[2];
10351             
10352             if(this.indicatorpos == 'right'){
10353                 cfg.cn = [
10354                     {
10355                         tag: 'label',
10356                         'for' :  id,
10357                         cls : 'control-label col-form-label',
10358                         cn : [
10359                             {
10360                                 tag : 'span',
10361                                 html : this.fieldLabel
10362                             },
10363                             indicator
10364                         ]
10365                     },
10366                     {
10367                         cls : "",
10368                         cn: [
10369                             inputblock
10370                         ]
10371                     }
10372
10373                 ];
10374                 
10375                 labelCfg = cfg.cn[0];
10376                 contentCfg = cfg.cn[1];
10377             
10378             }
10379             
10380             if(this.labelWidth > 12){
10381                 labelCfg.style = "width: " + this.labelWidth + 'px';
10382             }
10383             
10384             if(this.labelWidth < 13 && this.labelmd == 0){
10385                 this.labelmd = this.labelWidth;
10386             }
10387             
10388             if(this.labellg > 0){
10389                 labelCfg.cls += ' col-lg-' + this.labellg;
10390                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10391             }
10392             
10393             if(this.labelmd > 0){
10394                 labelCfg.cls += ' col-md-' + this.labelmd;
10395                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10396             }
10397             
10398             if(this.labelsm > 0){
10399                 labelCfg.cls += ' col-sm-' + this.labelsm;
10400                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10401             }
10402             
10403             if(this.labelxs > 0){
10404                 labelCfg.cls += ' col-xs-' + this.labelxs;
10405                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10406             }
10407             
10408             
10409         } else if ( this.fieldLabel.length) {
10410                 
10411             cfg.cn = [
10412                 {
10413                     tag : 'i',
10414                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10415                     tooltip : 'This field is required'
10416                 },
10417                 {
10418                     tag: 'label',
10419                    //cls : 'input-group-addon',
10420                     html : this.fieldLabel
10421
10422                 },
10423
10424                inputblock
10425
10426            ];
10427            
10428            if(this.indicatorpos == 'right'){
10429                 
10430                 cfg.cn = [
10431                     {
10432                         tag: 'label',
10433                        //cls : 'input-group-addon',
10434                         html : this.fieldLabel
10435
10436                     },
10437                     {
10438                         tag : 'i',
10439                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10440                         tooltip : 'This field is required'
10441                     },
10442
10443                    inputblock
10444
10445                ];
10446
10447             }
10448
10449         } else {
10450             
10451             cfg.cn = [
10452
10453                     inputblock
10454
10455             ];
10456                 
10457                 
10458         };
10459         
10460         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10461            cfg.cls += ' navbar-form';
10462         }
10463         
10464         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10465             // on BS4 we do this only if not form 
10466             cfg.cls += ' navbar-form';
10467             cfg.tag = 'li';
10468         }
10469         
10470         return cfg;
10471         
10472     },
10473     /**
10474      * return the real input element.
10475      */
10476     inputEl: function ()
10477     {
10478         return this.el.select('input.form-control',true).first();
10479     },
10480     
10481     tooltipEl : function()
10482     {
10483         return this.inputEl();
10484     },
10485     
10486     indicatorEl : function()
10487     {
10488         if (Roo.bootstrap.version == 4) {
10489             return false; // not enabled in v4 yet.
10490         }
10491         
10492         var indicator = this.el.select('i.roo-required-indicator',true).first();
10493         
10494         if(!indicator){
10495             return false;
10496         }
10497         
10498         return indicator;
10499         
10500     },
10501     
10502     setDisabled : function(v)
10503     {
10504         var i  = this.inputEl().dom;
10505         if (!v) {
10506             i.removeAttribute('disabled');
10507             return;
10508             
10509         }
10510         i.setAttribute('disabled','true');
10511     },
10512     initEvents : function()
10513     {
10514           
10515         this.inputEl().on("keydown" , this.fireKey,  this);
10516         this.inputEl().on("focus", this.onFocus,  this);
10517         this.inputEl().on("blur", this.onBlur,  this);
10518         
10519         this.inputEl().relayEvent('keyup', this);
10520         
10521         this.indicator = this.indicatorEl();
10522         
10523         if(this.indicator){
10524             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10525         }
10526  
10527         // reference to original value for reset
10528         this.originalValue = this.getValue();
10529         //Roo.form.TextField.superclass.initEvents.call(this);
10530         if(this.validationEvent == 'keyup'){
10531             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10532             this.inputEl().on('keyup', this.filterValidation, this);
10533         }
10534         else if(this.validationEvent !== false){
10535             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10536         }
10537         
10538         if(this.selectOnFocus){
10539             this.on("focus", this.preFocus, this);
10540             
10541         }
10542         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10543             this.inputEl().on("keypress", this.filterKeys, this);
10544         } else {
10545             this.inputEl().relayEvent('keypress', this);
10546         }
10547        /* if(this.grow){
10548             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10549             this.el.on("click", this.autoSize,  this);
10550         }
10551         */
10552         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10553             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10554         }
10555         
10556         if (typeof(this.before) == 'object') {
10557             this.before.render(this.el.select('.roo-input-before',true).first());
10558         }
10559         if (typeof(this.after) == 'object') {
10560             this.after.render(this.el.select('.roo-input-after',true).first());
10561         }
10562         
10563         this.inputEl().on('change', this.onChange, this);
10564         
10565     },
10566     filterValidation : function(e){
10567         if(!e.isNavKeyPress()){
10568             this.validationTask.delay(this.validationDelay);
10569         }
10570     },
10571      /**
10572      * Validates the field value
10573      * @return {Boolean} True if the value is valid, else false
10574      */
10575     validate : function(){
10576         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10577         if(this.disabled || this.validateValue(this.getRawValue())){
10578             this.markValid();
10579             return true;
10580         }
10581         
10582         this.markInvalid();
10583         return false;
10584     },
10585     
10586     
10587     /**
10588      * Validates a value according to the field's validation rules and marks the field as invalid
10589      * if the validation fails
10590      * @param {Mixed} value The value to validate
10591      * @return {Boolean} True if the value is valid, else false
10592      */
10593     validateValue : function(value)
10594     {
10595         if(this.getVisibilityEl().hasClass('hidden')){
10596             return true;
10597         }
10598         
10599         if(value.length < 1)  { // if it's blank
10600             if(this.allowBlank){
10601                 return true;
10602             }
10603             return false;
10604         }
10605         
10606         if(value.length < this.minLength){
10607             return false;
10608         }
10609         if(value.length > this.maxLength){
10610             return false;
10611         }
10612         if(this.vtype){
10613             var vt = Roo.form.VTypes;
10614             if(!vt[this.vtype](value, this)){
10615                 return false;
10616             }
10617         }
10618         if(typeof this.validator == "function"){
10619             var msg = this.validator(value);
10620             if(msg !== true){
10621                 return false;
10622             }
10623             if (typeof(msg) == 'string') {
10624                 this.invalidText = msg;
10625             }
10626         }
10627         
10628         if(this.regex && !this.regex.test(value)){
10629             return false;
10630         }
10631         
10632         return true;
10633     },
10634     
10635      // private
10636     fireKey : function(e){
10637         //Roo.log('field ' + e.getKey());
10638         if(e.isNavKeyPress()){
10639             this.fireEvent("specialkey", this, e);
10640         }
10641     },
10642     focus : function (selectText){
10643         if(this.rendered){
10644             this.inputEl().focus();
10645             if(selectText === true){
10646                 this.inputEl().dom.select();
10647             }
10648         }
10649         return this;
10650     } ,
10651     
10652     onFocus : function(){
10653         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10654            // this.el.addClass(this.focusClass);
10655         }
10656         if(!this.hasFocus){
10657             this.hasFocus = true;
10658             this.startValue = this.getValue();
10659             this.fireEvent("focus", this);
10660         }
10661     },
10662     
10663     beforeBlur : Roo.emptyFn,
10664
10665     
10666     // private
10667     onBlur : function(){
10668         this.beforeBlur();
10669         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10670             //this.el.removeClass(this.focusClass);
10671         }
10672         this.hasFocus = false;
10673         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10674             this.validate();
10675         }
10676         var v = this.getValue();
10677         if(String(v) !== String(this.startValue)){
10678             this.fireEvent('change', this, v, this.startValue);
10679         }
10680         this.fireEvent("blur", this);
10681     },
10682     
10683     onChange : function(e)
10684     {
10685         var v = this.getValue();
10686         if(String(v) !== String(this.startValue)){
10687             this.fireEvent('change', this, v, this.startValue);
10688         }
10689         
10690     },
10691     
10692     /**
10693      * Resets the current field value to the originally loaded value and clears any validation messages
10694      */
10695     reset : function(){
10696         this.setValue(this.originalValue);
10697         this.validate();
10698     },
10699      /**
10700      * Returns the name of the field
10701      * @return {Mixed} name The name field
10702      */
10703     getName: function(){
10704         return this.name;
10705     },
10706      /**
10707      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10708      * @return {Mixed} value The field value
10709      */
10710     getValue : function(){
10711         
10712         var v = this.inputEl().getValue();
10713         
10714         return v;
10715     },
10716     /**
10717      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10718      * @return {Mixed} value The field value
10719      */
10720     getRawValue : function(){
10721         var v = this.inputEl().getValue();
10722         
10723         return v;
10724     },
10725     
10726     /**
10727      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10728      * @param {Mixed} value The value to set
10729      */
10730     setRawValue : function(v){
10731         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10732     },
10733     
10734     selectText : function(start, end){
10735         var v = this.getRawValue();
10736         if(v.length > 0){
10737             start = start === undefined ? 0 : start;
10738             end = end === undefined ? v.length : end;
10739             var d = this.inputEl().dom;
10740             if(d.setSelectionRange){
10741                 d.setSelectionRange(start, end);
10742             }else if(d.createTextRange){
10743                 var range = d.createTextRange();
10744                 range.moveStart("character", start);
10745                 range.moveEnd("character", v.length-end);
10746                 range.select();
10747             }
10748         }
10749     },
10750     
10751     /**
10752      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10753      * @param {Mixed} value The value to set
10754      */
10755     setValue : function(v){
10756         this.value = v;
10757         if(this.rendered){
10758             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10759             this.validate();
10760         }
10761     },
10762     
10763     /*
10764     processValue : function(value){
10765         if(this.stripCharsRe){
10766             var newValue = value.replace(this.stripCharsRe, '');
10767             if(newValue !== value){
10768                 this.setRawValue(newValue);
10769                 return newValue;
10770             }
10771         }
10772         return value;
10773     },
10774   */
10775     preFocus : function(){
10776         
10777         if(this.selectOnFocus){
10778             this.inputEl().dom.select();
10779         }
10780     },
10781     filterKeys : function(e){
10782         var k = e.getKey();
10783         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10784             return;
10785         }
10786         var c = e.getCharCode(), cc = String.fromCharCode(c);
10787         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10788             return;
10789         }
10790         if(!this.maskRe.test(cc)){
10791             e.stopEvent();
10792         }
10793     },
10794      /**
10795      * Clear any invalid styles/messages for this field
10796      */
10797     clearInvalid : function(){
10798         
10799         if(!this.el || this.preventMark){ // not rendered
10800             return;
10801         }
10802         
10803         
10804         this.el.removeClass([this.invalidClass, 'is-invalid']);
10805         
10806         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10807             
10808             var feedback = this.el.select('.form-control-feedback', true).first();
10809             
10810             if(feedback){
10811                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10812             }
10813             
10814         }
10815         
10816         if(this.indicator){
10817             this.indicator.removeClass('visible');
10818             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10819         }
10820         
10821         this.fireEvent('valid', this);
10822     },
10823     
10824      /**
10825      * Mark this field as valid
10826      */
10827     markValid : function()
10828     {
10829         if(!this.el  || this.preventMark){ // not rendered...
10830             return;
10831         }
10832         
10833         this.el.removeClass([this.invalidClass, this.validClass]);
10834         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10835
10836         var feedback = this.el.select('.form-control-feedback', true).first();
10837             
10838         if(feedback){
10839             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10840         }
10841         
10842         if(this.indicator){
10843             this.indicator.removeClass('visible');
10844             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10845         }
10846         
10847         if(this.disabled){
10848             return;
10849         }
10850         
10851         if(this.allowBlank && !this.getRawValue().length){
10852             return;
10853         }
10854         if (Roo.bootstrap.version == 3) {
10855             this.el.addClass(this.validClass);
10856         } else {
10857             this.inputEl().addClass('is-valid');
10858         }
10859
10860         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10861             
10862             var feedback = this.el.select('.form-control-feedback', true).first();
10863             
10864             if(feedback){
10865                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10866                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10867             }
10868             
10869         }
10870         
10871         this.fireEvent('valid', this);
10872     },
10873     
10874      /**
10875      * Mark this field as invalid
10876      * @param {String} msg The validation message
10877      */
10878     markInvalid : function(msg)
10879     {
10880         if(!this.el  || this.preventMark){ // not rendered
10881             return;
10882         }
10883         
10884         this.el.removeClass([this.invalidClass, this.validClass]);
10885         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10886         
10887         var feedback = this.el.select('.form-control-feedback', true).first();
10888             
10889         if(feedback){
10890             this.el.select('.form-control-feedback', true).first().removeClass(
10891                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10892         }
10893
10894         if(this.disabled){
10895             return;
10896         }
10897         
10898         if(this.allowBlank && !this.getRawValue().length){
10899             return;
10900         }
10901         
10902         if(this.indicator){
10903             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10904             this.indicator.addClass('visible');
10905         }
10906         if (Roo.bootstrap.version == 3) {
10907             this.el.addClass(this.invalidClass);
10908         } else {
10909             this.inputEl().addClass('is-invalid');
10910         }
10911         
10912         
10913         
10914         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10915             
10916             var feedback = this.el.select('.form-control-feedback', true).first();
10917             
10918             if(feedback){
10919                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10920                 
10921                 if(this.getValue().length || this.forceFeedback){
10922                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10923                 }
10924                 
10925             }
10926             
10927         }
10928         
10929         this.fireEvent('invalid', this, msg);
10930     },
10931     // private
10932     SafariOnKeyDown : function(event)
10933     {
10934         // this is a workaround for a password hang bug on chrome/ webkit.
10935         if (this.inputEl().dom.type != 'password') {
10936             return;
10937         }
10938         
10939         var isSelectAll = false;
10940         
10941         if(this.inputEl().dom.selectionEnd > 0){
10942             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10943         }
10944         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10945             event.preventDefault();
10946             this.setValue('');
10947             return;
10948         }
10949         
10950         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10951             
10952             event.preventDefault();
10953             // this is very hacky as keydown always get's upper case.
10954             //
10955             var cc = String.fromCharCode(event.getCharCode());
10956             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10957             
10958         }
10959     },
10960     adjustWidth : function(tag, w){
10961         tag = tag.toLowerCase();
10962         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10963             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10964                 if(tag == 'input'){
10965                     return w + 2;
10966                 }
10967                 if(tag == 'textarea'){
10968                     return w-2;
10969                 }
10970             }else if(Roo.isOpera){
10971                 if(tag == 'input'){
10972                     return w + 2;
10973                 }
10974                 if(tag == 'textarea'){
10975                     return w-2;
10976                 }
10977             }
10978         }
10979         return w;
10980     },
10981     
10982     setFieldLabel : function(v)
10983     {
10984         if(!this.rendered){
10985             return;
10986         }
10987         
10988         if(this.indicatorEl()){
10989             var ar = this.el.select('label > span',true);
10990             
10991             if (ar.elements.length) {
10992                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10993                 this.fieldLabel = v;
10994                 return;
10995             }
10996             
10997             var br = this.el.select('label',true);
10998             
10999             if(br.elements.length) {
11000                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11001                 this.fieldLabel = v;
11002                 return;
11003             }
11004             
11005             Roo.log('Cannot Found any of label > span || label in input');
11006             return;
11007         }
11008         
11009         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11010         this.fieldLabel = v;
11011         
11012         
11013     }
11014 });
11015
11016  
11017 /*
11018  * - LGPL
11019  *
11020  * Input
11021  * 
11022  */
11023
11024 /**
11025  * @class Roo.bootstrap.TextArea
11026  * @extends Roo.bootstrap.Input
11027  * Bootstrap TextArea class
11028  * @cfg {Number} cols Specifies the visible width of a text area
11029  * @cfg {Number} rows Specifies the visible number of lines in a text area
11030  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11031  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11032  * @cfg {string} html text
11033  * 
11034  * @constructor
11035  * Create a new TextArea
11036  * @param {Object} config The config object
11037  */
11038
11039 Roo.bootstrap.TextArea = function(config){
11040     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11041    
11042 };
11043
11044 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11045      
11046     cols : false,
11047     rows : 5,
11048     readOnly : false,
11049     warp : 'soft',
11050     resize : false,
11051     value: false,
11052     html: false,
11053     
11054     getAutoCreate : function(){
11055         
11056         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11057         
11058         var id = Roo.id();
11059         
11060         var cfg = {};
11061         
11062         if(this.inputType != 'hidden'){
11063             cfg.cls = 'form-group' //input-group
11064         }
11065         
11066         var input =  {
11067             tag: 'textarea',
11068             id : id,
11069             warp : this.warp,
11070             rows : this.rows,
11071             value : this.value || '',
11072             html: this.html || '',
11073             cls : 'form-control',
11074             placeholder : this.placeholder || '' 
11075             
11076         };
11077         
11078         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11079             input.maxLength = this.maxLength;
11080         }
11081         
11082         if(this.resize){
11083             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11084         }
11085         
11086         if(this.cols){
11087             input.cols = this.cols;
11088         }
11089         
11090         if (this.readOnly) {
11091             input.readonly = true;
11092         }
11093         
11094         if (this.name) {
11095             input.name = this.name;
11096         }
11097         
11098         if (this.size) {
11099             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11100         }
11101         
11102         var settings=this;
11103         ['xs','sm','md','lg'].map(function(size){
11104             if (settings[size]) {
11105                 cfg.cls += ' col-' + size + '-' + settings[size];
11106             }
11107         });
11108         
11109         var inputblock = input;
11110         
11111         if(this.hasFeedback && !this.allowBlank){
11112             
11113             var feedback = {
11114                 tag: 'span',
11115                 cls: 'glyphicon form-control-feedback'
11116             };
11117
11118             inputblock = {
11119                 cls : 'has-feedback',
11120                 cn :  [
11121                     input,
11122                     feedback
11123                 ] 
11124             };  
11125         }
11126         
11127         
11128         if (this.before || this.after) {
11129             
11130             inputblock = {
11131                 cls : 'input-group',
11132                 cn :  [] 
11133             };
11134             if (this.before) {
11135                 inputblock.cn.push({
11136                     tag :'span',
11137                     cls : 'input-group-addon',
11138                     html : this.before
11139                 });
11140             }
11141             
11142             inputblock.cn.push(input);
11143             
11144             if(this.hasFeedback && !this.allowBlank){
11145                 inputblock.cls += ' has-feedback';
11146                 inputblock.cn.push(feedback);
11147             }
11148             
11149             if (this.after) {
11150                 inputblock.cn.push({
11151                     tag :'span',
11152                     cls : 'input-group-addon',
11153                     html : this.after
11154                 });
11155             }
11156             
11157         }
11158         
11159         if (align ==='left' && this.fieldLabel.length) {
11160             cfg.cn = [
11161                 {
11162                     tag: 'label',
11163                     'for' :  id,
11164                     cls : 'control-label',
11165                     html : this.fieldLabel
11166                 },
11167                 {
11168                     cls : "",
11169                     cn: [
11170                         inputblock
11171                     ]
11172                 }
11173
11174             ];
11175             
11176             if(this.labelWidth > 12){
11177                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11178             }
11179
11180             if(this.labelWidth < 13 && this.labelmd == 0){
11181                 this.labelmd = this.labelWidth;
11182             }
11183
11184             if(this.labellg > 0){
11185                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11186                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11187             }
11188
11189             if(this.labelmd > 0){
11190                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11191                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11192             }
11193
11194             if(this.labelsm > 0){
11195                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11196                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11197             }
11198
11199             if(this.labelxs > 0){
11200                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11201                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11202             }
11203             
11204         } else if ( this.fieldLabel.length) {
11205             cfg.cn = [
11206
11207                {
11208                    tag: 'label',
11209                    //cls : 'input-group-addon',
11210                    html : this.fieldLabel
11211
11212                },
11213
11214                inputblock
11215
11216            ];
11217
11218         } else {
11219
11220             cfg.cn = [
11221
11222                 inputblock
11223
11224             ];
11225                 
11226         }
11227         
11228         if (this.disabled) {
11229             input.disabled=true;
11230         }
11231         
11232         return cfg;
11233         
11234     },
11235     /**
11236      * return the real textarea element.
11237      */
11238     inputEl: function ()
11239     {
11240         return this.el.select('textarea.form-control',true).first();
11241     },
11242     
11243     /**
11244      * Clear any invalid styles/messages for this field
11245      */
11246     clearInvalid : function()
11247     {
11248         
11249         if(!this.el || this.preventMark){ // not rendered
11250             return;
11251         }
11252         
11253         var label = this.el.select('label', true).first();
11254         var icon = this.el.select('i.fa-star', true).first();
11255         
11256         if(label && icon){
11257             icon.remove();
11258         }
11259         this.el.removeClass( this.validClass);
11260         this.inputEl().removeClass('is-invalid');
11261          
11262         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11263             
11264             var feedback = this.el.select('.form-control-feedback', true).first();
11265             
11266             if(feedback){
11267                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11268             }
11269             
11270         }
11271         
11272         this.fireEvent('valid', this);
11273     },
11274     
11275      /**
11276      * Mark this field as valid
11277      */
11278     markValid : function()
11279     {
11280         if(!this.el  || this.preventMark){ // not rendered
11281             return;
11282         }
11283         
11284         this.el.removeClass([this.invalidClass, this.validClass]);
11285         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11286         
11287         var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289         if(feedback){
11290             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11291         }
11292
11293         if(this.disabled || this.allowBlank){
11294             return;
11295         }
11296         
11297         var label = this.el.select('label', true).first();
11298         var icon = this.el.select('i.fa-star', true).first();
11299         
11300         if(label && icon){
11301             icon.remove();
11302         }
11303         if (Roo.bootstrap.version == 3) {
11304             this.el.addClass(this.validClass);
11305         } else {
11306             this.inputEl().addClass('is-valid');
11307         }
11308         
11309         
11310         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11311             
11312             var feedback = this.el.select('.form-control-feedback', true).first();
11313             
11314             if(feedback){
11315                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11316                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11317             }
11318             
11319         }
11320         
11321         this.fireEvent('valid', this);
11322     },
11323     
11324      /**
11325      * Mark this field as invalid
11326      * @param {String} msg The validation message
11327      */
11328     markInvalid : function(msg)
11329     {
11330         if(!this.el  || this.preventMark){ // not rendered
11331             return;
11332         }
11333         
11334         this.el.removeClass([this.invalidClass, this.validClass]);
11335         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11336         
11337         var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339         if(feedback){
11340             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341         }
11342
11343         if(this.disabled || this.allowBlank){
11344             return;
11345         }
11346         
11347         var label = this.el.select('label', true).first();
11348         var icon = this.el.select('i.fa-star', true).first();
11349         
11350         if(!this.getValue().length && label && !icon){
11351             this.el.createChild({
11352                 tag : 'i',
11353                 cls : 'text-danger fa fa-lg fa-star',
11354                 tooltip : 'This field is required',
11355                 style : 'margin-right:5px;'
11356             }, label, true);
11357         }
11358         
11359         if (Roo.bootstrap.version == 3) {
11360             this.el.addClass(this.invalidClass);
11361         } else {
11362             this.inputEl().addClass('is-invalid');
11363         }
11364         
11365         // fixme ... this may be depricated need to test..
11366         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11367             
11368             var feedback = this.el.select('.form-control-feedback', true).first();
11369             
11370             if(feedback){
11371                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11372                 
11373                 if(this.getValue().length || this.forceFeedback){
11374                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11375                 }
11376                 
11377             }
11378             
11379         }
11380         
11381         this.fireEvent('invalid', this, msg);
11382     }
11383 });
11384
11385  
11386 /*
11387  * - LGPL
11388  *
11389  * trigger field - base class for combo..
11390  * 
11391  */
11392  
11393 /**
11394  * @class Roo.bootstrap.TriggerField
11395  * @extends Roo.bootstrap.Input
11396  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11397  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11398  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11399  * for which you can provide a custom implementation.  For example:
11400  * <pre><code>
11401 var trigger = new Roo.bootstrap.TriggerField();
11402 trigger.onTriggerClick = myTriggerFn;
11403 trigger.applyTo('my-field');
11404 </code></pre>
11405  *
11406  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11407  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11408  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11409  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11410  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11411
11412  * @constructor
11413  * Create a new TriggerField.
11414  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11415  * to the base TextField)
11416  */
11417 Roo.bootstrap.TriggerField = function(config){
11418     this.mimicing = false;
11419     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11420 };
11421
11422 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11423     /**
11424      * @cfg {String} triggerClass A CSS class to apply to the trigger
11425      */
11426      /**
11427      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11428      */
11429     hideTrigger:false,
11430
11431     /**
11432      * @cfg {Boolean} removable (true|false) special filter default false
11433      */
11434     removable : false,
11435     
11436     /** @cfg {Boolean} grow @hide */
11437     /** @cfg {Number} growMin @hide */
11438     /** @cfg {Number} growMax @hide */
11439
11440     /**
11441      * @hide 
11442      * @method
11443      */
11444     autoSize: Roo.emptyFn,
11445     // private
11446     monitorTab : true,
11447     // private
11448     deferHeight : true,
11449
11450     
11451     actionMode : 'wrap',
11452     
11453     caret : false,
11454     
11455     
11456     getAutoCreate : function(){
11457        
11458         var align = this.labelAlign || this.parentLabelAlign();
11459         
11460         var id = Roo.id();
11461         
11462         var cfg = {
11463             cls: 'form-group' //input-group
11464         };
11465         
11466         
11467         var input =  {
11468             tag: 'input',
11469             id : id,
11470             type : this.inputType,
11471             cls : 'form-control',
11472             autocomplete: 'new-password',
11473             placeholder : this.placeholder || '' 
11474             
11475         };
11476         if (this.name) {
11477             input.name = this.name;
11478         }
11479         if (this.size) {
11480             input.cls += ' input-' + this.size;
11481         }
11482         
11483         if (this.disabled) {
11484             input.disabled=true;
11485         }
11486         
11487         var inputblock = input;
11488         
11489         if(this.hasFeedback && !this.allowBlank){
11490             
11491             var feedback = {
11492                 tag: 'span',
11493                 cls: 'glyphicon form-control-feedback'
11494             };
11495             
11496             if(this.removable && !this.editable && !this.tickable){
11497                 inputblock = {
11498                     cls : 'has-feedback',
11499                     cn :  [
11500                         inputblock,
11501                         {
11502                             tag: 'button',
11503                             html : 'x',
11504                             cls : 'roo-combo-removable-btn close'
11505                         },
11506                         feedback
11507                     ] 
11508                 };
11509             } else {
11510                 inputblock = {
11511                     cls : 'has-feedback',
11512                     cn :  [
11513                         inputblock,
11514                         feedback
11515                     ] 
11516                 };
11517             }
11518
11519         } else {
11520             if(this.removable && !this.editable && !this.tickable){
11521                 inputblock = {
11522                     cls : 'roo-removable',
11523                     cn :  [
11524                         inputblock,
11525                         {
11526                             tag: 'button',
11527                             html : 'x',
11528                             cls : 'roo-combo-removable-btn close'
11529                         }
11530                     ] 
11531                 };
11532             }
11533         }
11534         
11535         if (this.before || this.after) {
11536             
11537             inputblock = {
11538                 cls : 'input-group',
11539                 cn :  [] 
11540             };
11541             if (this.before) {
11542                 inputblock.cn.push({
11543                     tag :'span',
11544                     cls : 'input-group-addon input-group-prepend input-group-text',
11545                     html : this.before
11546                 });
11547             }
11548             
11549             inputblock.cn.push(input);
11550             
11551             if(this.hasFeedback && !this.allowBlank){
11552                 inputblock.cls += ' has-feedback';
11553                 inputblock.cn.push(feedback);
11554             }
11555             
11556             if (this.after) {
11557                 inputblock.cn.push({
11558                     tag :'span',
11559                     cls : 'input-group-addon input-group-append input-group-text',
11560                     html : this.after
11561                 });
11562             }
11563             
11564         };
11565         
11566       
11567         
11568         var ibwrap = inputblock;
11569         
11570         if(this.multiple){
11571             ibwrap = {
11572                 tag: 'ul',
11573                 cls: 'roo-select2-choices',
11574                 cn:[
11575                     {
11576                         tag: 'li',
11577                         cls: 'roo-select2-search-field',
11578                         cn: [
11579
11580                             inputblock
11581                         ]
11582                     }
11583                 ]
11584             };
11585                 
11586         }
11587         
11588         var combobox = {
11589             cls: 'roo-select2-container input-group',
11590             cn: [
11591                  {
11592                     tag: 'input',
11593                     type : 'hidden',
11594                     cls: 'form-hidden-field'
11595                 },
11596                 ibwrap
11597             ]
11598         };
11599         
11600         if(!this.multiple && this.showToggleBtn){
11601             
11602             var caret = {
11603                         tag: 'span',
11604                         cls: 'caret'
11605              };
11606             if (this.caret != false) {
11607                 caret = {
11608                      tag: 'i',
11609                      cls: 'fa fa-' + this.caret
11610                 };
11611                 
11612             }
11613             
11614             combobox.cn.push({
11615                 tag :'span',
11616                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11617                 cn : [
11618                     Roo.bootstrap.version == 3 ? caret : '',
11619                     {
11620                         tag: 'span',
11621                         cls: 'combobox-clear',
11622                         cn  : [
11623                             {
11624                                 tag : 'i',
11625                                 cls: 'icon-remove'
11626                             }
11627                         ]
11628                     }
11629                 ]
11630
11631             })
11632         }
11633         
11634         if(this.multiple){
11635             combobox.cls += ' roo-select2-container-multi';
11636         }
11637          var indicator = {
11638             tag : 'i',
11639             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11640             tooltip : 'This field is required'
11641         };
11642         if (Roo.bootstrap.version == 4) {
11643             indicator = {
11644                 tag : 'i',
11645                 style : 'display:none'
11646             };
11647         }
11648         
11649         
11650         if (align ==='left' && this.fieldLabel.length) {
11651             
11652             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11653
11654             cfg.cn = [
11655                 indicator,
11656                 {
11657                     tag: 'label',
11658                     'for' :  id,
11659                     cls : 'control-label',
11660                     html : this.fieldLabel
11661
11662                 },
11663                 {
11664                     cls : "", 
11665                     cn: [
11666                         combobox
11667                     ]
11668                 }
11669
11670             ];
11671             
11672             var labelCfg = cfg.cn[1];
11673             var contentCfg = cfg.cn[2];
11674             
11675             if(this.indicatorpos == 'right'){
11676                 cfg.cn = [
11677                     {
11678                         tag: 'label',
11679                         'for' :  id,
11680                         cls : 'control-label',
11681                         cn : [
11682                             {
11683                                 tag : 'span',
11684                                 html : this.fieldLabel
11685                             },
11686                             indicator
11687                         ]
11688                     },
11689                     {
11690                         cls : "", 
11691                         cn: [
11692                             combobox
11693                         ]
11694                     }
11695
11696                 ];
11697                 
11698                 labelCfg = cfg.cn[0];
11699                 contentCfg = cfg.cn[1];
11700             }
11701             
11702             if(this.labelWidth > 12){
11703                 labelCfg.style = "width: " + this.labelWidth + 'px';
11704             }
11705             
11706             if(this.labelWidth < 13 && this.labelmd == 0){
11707                 this.labelmd = this.labelWidth;
11708             }
11709             
11710             if(this.labellg > 0){
11711                 labelCfg.cls += ' col-lg-' + this.labellg;
11712                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11713             }
11714             
11715             if(this.labelmd > 0){
11716                 labelCfg.cls += ' col-md-' + this.labelmd;
11717                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11718             }
11719             
11720             if(this.labelsm > 0){
11721                 labelCfg.cls += ' col-sm-' + this.labelsm;
11722                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11723             }
11724             
11725             if(this.labelxs > 0){
11726                 labelCfg.cls += ' col-xs-' + this.labelxs;
11727                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11728             }
11729             
11730         } else if ( this.fieldLabel.length) {
11731 //                Roo.log(" label");
11732             cfg.cn = [
11733                 indicator,
11734                {
11735                    tag: 'label',
11736                    //cls : 'input-group-addon',
11737                    html : this.fieldLabel
11738
11739                },
11740
11741                combobox
11742
11743             ];
11744             
11745             if(this.indicatorpos == 'right'){
11746                 
11747                 cfg.cn = [
11748                     {
11749                        tag: 'label',
11750                        cn : [
11751                            {
11752                                tag : 'span',
11753                                html : this.fieldLabel
11754                            },
11755                            indicator
11756                        ]
11757
11758                     },
11759                     combobox
11760
11761                 ];
11762
11763             }
11764
11765         } else {
11766             
11767 //                Roo.log(" no label && no align");
11768                 cfg = combobox
11769                      
11770                 
11771         }
11772         
11773         var settings=this;
11774         ['xs','sm','md','lg'].map(function(size){
11775             if (settings[size]) {
11776                 cfg.cls += ' col-' + size + '-' + settings[size];
11777             }
11778         });
11779         
11780         return cfg;
11781         
11782     },
11783     
11784     
11785     
11786     // private
11787     onResize : function(w, h){
11788 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11789 //        if(typeof w == 'number'){
11790 //            var x = w - this.trigger.getWidth();
11791 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11792 //            this.trigger.setStyle('left', x+'px');
11793 //        }
11794     },
11795
11796     // private
11797     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11798
11799     // private
11800     getResizeEl : function(){
11801         return this.inputEl();
11802     },
11803
11804     // private
11805     getPositionEl : function(){
11806         return this.inputEl();
11807     },
11808
11809     // private
11810     alignErrorIcon : function(){
11811         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11812     },
11813
11814     // private
11815     initEvents : function(){
11816         
11817         this.createList();
11818         
11819         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11820         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11821         if(!this.multiple && this.showToggleBtn){
11822             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11823             if(this.hideTrigger){
11824                 this.trigger.setDisplayed(false);
11825             }
11826             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11827         }
11828         
11829         if(this.multiple){
11830             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11831         }
11832         
11833         if(this.removable && !this.editable && !this.tickable){
11834             var close = this.closeTriggerEl();
11835             
11836             if(close){
11837                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11838                 close.on('click', this.removeBtnClick, this, close);
11839             }
11840         }
11841         
11842         //this.trigger.addClassOnOver('x-form-trigger-over');
11843         //this.trigger.addClassOnClick('x-form-trigger-click');
11844         
11845         //if(!this.width){
11846         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11847         //}
11848     },
11849     
11850     closeTriggerEl : function()
11851     {
11852         var close = this.el.select('.roo-combo-removable-btn', true).first();
11853         return close ? close : false;
11854     },
11855     
11856     removeBtnClick : function(e, h, el)
11857     {
11858         e.preventDefault();
11859         
11860         if(this.fireEvent("remove", this) !== false){
11861             this.reset();
11862             this.fireEvent("afterremove", this)
11863         }
11864     },
11865     
11866     createList : function()
11867     {
11868         this.list = Roo.get(document.body).createChild({
11869             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11870             cls: 'typeahead typeahead-long dropdown-menu',
11871             style: 'display:none'
11872         });
11873         
11874         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11875         
11876     },
11877
11878     // private
11879     initTrigger : function(){
11880        
11881     },
11882
11883     // private
11884     onDestroy : function(){
11885         if(this.trigger){
11886             this.trigger.removeAllListeners();
11887           //  this.trigger.remove();
11888         }
11889         //if(this.wrap){
11890         //    this.wrap.remove();
11891         //}
11892         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11893     },
11894
11895     // private
11896     onFocus : function(){
11897         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11898         /*
11899         if(!this.mimicing){
11900             this.wrap.addClass('x-trigger-wrap-focus');
11901             this.mimicing = true;
11902             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11903             if(this.monitorTab){
11904                 this.el.on("keydown", this.checkTab, this);
11905             }
11906         }
11907         */
11908     },
11909
11910     // private
11911     checkTab : function(e){
11912         if(e.getKey() == e.TAB){
11913             this.triggerBlur();
11914         }
11915     },
11916
11917     // private
11918     onBlur : function(){
11919         // do nothing
11920     },
11921
11922     // private
11923     mimicBlur : function(e, t){
11924         /*
11925         if(!this.wrap.contains(t) && this.validateBlur()){
11926             this.triggerBlur();
11927         }
11928         */
11929     },
11930
11931     // private
11932     triggerBlur : function(){
11933         this.mimicing = false;
11934         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11935         if(this.monitorTab){
11936             this.el.un("keydown", this.checkTab, this);
11937         }
11938         //this.wrap.removeClass('x-trigger-wrap-focus');
11939         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11940     },
11941
11942     // private
11943     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11944     validateBlur : function(e, t){
11945         return true;
11946     },
11947
11948     // private
11949     onDisable : function(){
11950         this.inputEl().dom.disabled = true;
11951         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11952         //if(this.wrap){
11953         //    this.wrap.addClass('x-item-disabled');
11954         //}
11955     },
11956
11957     // private
11958     onEnable : function(){
11959         this.inputEl().dom.disabled = false;
11960         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11961         //if(this.wrap){
11962         //    this.el.removeClass('x-item-disabled');
11963         //}
11964     },
11965
11966     // private
11967     onShow : function(){
11968         var ae = this.getActionEl();
11969         
11970         if(ae){
11971             ae.dom.style.display = '';
11972             ae.dom.style.visibility = 'visible';
11973         }
11974     },
11975
11976     // private
11977     
11978     onHide : function(){
11979         var ae = this.getActionEl();
11980         ae.dom.style.display = 'none';
11981     },
11982
11983     /**
11984      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11985      * by an implementing function.
11986      * @method
11987      * @param {EventObject} e
11988      */
11989     onTriggerClick : Roo.emptyFn
11990 });
11991  /*
11992  * Based on:
11993  * Ext JS Library 1.1.1
11994  * Copyright(c) 2006-2007, Ext JS, LLC.
11995  *
11996  * Originally Released Under LGPL - original licence link has changed is not relivant.
11997  *
11998  * Fork - LGPL
11999  * <script type="text/javascript">
12000  */
12001
12002
12003 /**
12004  * @class Roo.data.SortTypes
12005  * @singleton
12006  * Defines the default sorting (casting?) comparison functions used when sorting data.
12007  */
12008 Roo.data.SortTypes = {
12009     /**
12010      * Default sort that does nothing
12011      * @param {Mixed} s The value being converted
12012      * @return {Mixed} The comparison value
12013      */
12014     none : function(s){
12015         return s;
12016     },
12017     
12018     /**
12019      * The regular expression used to strip tags
12020      * @type {RegExp}
12021      * @property
12022      */
12023     stripTagsRE : /<\/?[^>]+>/gi,
12024     
12025     /**
12026      * Strips all HTML tags to sort on text only
12027      * @param {Mixed} s The value being converted
12028      * @return {String} The comparison value
12029      */
12030     asText : function(s){
12031         return String(s).replace(this.stripTagsRE, "");
12032     },
12033     
12034     /**
12035      * Strips all HTML tags to sort on text only - Case insensitive
12036      * @param {Mixed} s The value being converted
12037      * @return {String} The comparison value
12038      */
12039     asUCText : function(s){
12040         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12041     },
12042     
12043     /**
12044      * Case insensitive string
12045      * @param {Mixed} s The value being converted
12046      * @return {String} The comparison value
12047      */
12048     asUCString : function(s) {
12049         return String(s).toUpperCase();
12050     },
12051     
12052     /**
12053      * Date sorting
12054      * @param {Mixed} s The value being converted
12055      * @return {Number} The comparison value
12056      */
12057     asDate : function(s) {
12058         if(!s){
12059             return 0;
12060         }
12061         if(s instanceof Date){
12062             return s.getTime();
12063         }
12064         return Date.parse(String(s));
12065     },
12066     
12067     /**
12068      * Float sorting
12069      * @param {Mixed} s The value being converted
12070      * @return {Float} The comparison value
12071      */
12072     asFloat : function(s) {
12073         var val = parseFloat(String(s).replace(/,/g, ""));
12074         if(isNaN(val)) {
12075             val = 0;
12076         }
12077         return val;
12078     },
12079     
12080     /**
12081      * Integer sorting
12082      * @param {Mixed} s The value being converted
12083      * @return {Number} The comparison value
12084      */
12085     asInt : function(s) {
12086         var val = parseInt(String(s).replace(/,/g, ""));
12087         if(isNaN(val)) {
12088             val = 0;
12089         }
12090         return val;
12091     }
12092 };/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104 * @class Roo.data.Record
12105  * Instances of this class encapsulate both record <em>definition</em> information, and record
12106  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12107  * to access Records cached in an {@link Roo.data.Store} object.<br>
12108  * <p>
12109  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12110  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12111  * objects.<br>
12112  * <p>
12113  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12114  * @constructor
12115  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12116  * {@link #create}. The parameters are the same.
12117  * @param {Array} data An associative Array of data values keyed by the field name.
12118  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12119  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12120  * not specified an integer id is generated.
12121  */
12122 Roo.data.Record = function(data, id){
12123     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12124     this.data = data;
12125 };
12126
12127 /**
12128  * Generate a constructor for a specific record layout.
12129  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12130  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12131  * Each field definition object may contain the following properties: <ul>
12132  * <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,
12133  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12134  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12135  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12136  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12137  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12138  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12139  * this may be omitted.</p></li>
12140  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12141  * <ul><li>auto (Default, implies no conversion)</li>
12142  * <li>string</li>
12143  * <li>int</li>
12144  * <li>float</li>
12145  * <li>boolean</li>
12146  * <li>date</li></ul></p></li>
12147  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12148  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12149  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12150  * by the Reader into an object that will be stored in the Record. It is passed the
12151  * following parameters:<ul>
12152  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12153  * </ul></p></li>
12154  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12155  * </ul>
12156  * <br>usage:<br><pre><code>
12157 var TopicRecord = Roo.data.Record.create(
12158     {name: 'title', mapping: 'topic_title'},
12159     {name: 'author', mapping: 'username'},
12160     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12161     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12162     {name: 'lastPoster', mapping: 'user2'},
12163     {name: 'excerpt', mapping: 'post_text'}
12164 );
12165
12166 var myNewRecord = new TopicRecord({
12167     title: 'Do my job please',
12168     author: 'noobie',
12169     totalPosts: 1,
12170     lastPost: new Date(),
12171     lastPoster: 'Animal',
12172     excerpt: 'No way dude!'
12173 });
12174 myStore.add(myNewRecord);
12175 </code></pre>
12176  * @method create
12177  * @static
12178  */
12179 Roo.data.Record.create = function(o){
12180     var f = function(){
12181         f.superclass.constructor.apply(this, arguments);
12182     };
12183     Roo.extend(f, Roo.data.Record);
12184     var p = f.prototype;
12185     p.fields = new Roo.util.MixedCollection(false, function(field){
12186         return field.name;
12187     });
12188     for(var i = 0, len = o.length; i < len; i++){
12189         p.fields.add(new Roo.data.Field(o[i]));
12190     }
12191     f.getField = function(name){
12192         return p.fields.get(name);  
12193     };
12194     return f;
12195 };
12196
12197 Roo.data.Record.AUTO_ID = 1000;
12198 Roo.data.Record.EDIT = 'edit';
12199 Roo.data.Record.REJECT = 'reject';
12200 Roo.data.Record.COMMIT = 'commit';
12201
12202 Roo.data.Record.prototype = {
12203     /**
12204      * Readonly flag - true if this record has been modified.
12205      * @type Boolean
12206      */
12207     dirty : false,
12208     editing : false,
12209     error: null,
12210     modified: null,
12211
12212     // private
12213     join : function(store){
12214         this.store = store;
12215     },
12216
12217     /**
12218      * Set the named field to the specified value.
12219      * @param {String} name The name of the field to set.
12220      * @param {Object} value The value to set the field to.
12221      */
12222     set : function(name, value){
12223         if(this.data[name] == value){
12224             return;
12225         }
12226         this.dirty = true;
12227         if(!this.modified){
12228             this.modified = {};
12229         }
12230         if(typeof this.modified[name] == 'undefined'){
12231             this.modified[name] = this.data[name];
12232         }
12233         this.data[name] = value;
12234         if(!this.editing && this.store){
12235             this.store.afterEdit(this);
12236         }       
12237     },
12238
12239     /**
12240      * Get the value of the named field.
12241      * @param {String} name The name of the field to get the value of.
12242      * @return {Object} The value of the field.
12243      */
12244     get : function(name){
12245         return this.data[name]; 
12246     },
12247
12248     // private
12249     beginEdit : function(){
12250         this.editing = true;
12251         this.modified = {}; 
12252     },
12253
12254     // private
12255     cancelEdit : function(){
12256         this.editing = false;
12257         delete this.modified;
12258     },
12259
12260     // private
12261     endEdit : function(){
12262         this.editing = false;
12263         if(this.dirty && this.store){
12264             this.store.afterEdit(this);
12265         }
12266     },
12267
12268     /**
12269      * Usually called by the {@link Roo.data.Store} which owns the Record.
12270      * Rejects all changes made to the Record since either creation, or the last commit operation.
12271      * Modified fields are reverted to their original values.
12272      * <p>
12273      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12274      * of reject operations.
12275      */
12276     reject : function(){
12277         var m = this.modified;
12278         for(var n in m){
12279             if(typeof m[n] != "function"){
12280                 this.data[n] = m[n];
12281             }
12282         }
12283         this.dirty = false;
12284         delete this.modified;
12285         this.editing = false;
12286         if(this.store){
12287             this.store.afterReject(this);
12288         }
12289     },
12290
12291     /**
12292      * Usually called by the {@link Roo.data.Store} which owns the Record.
12293      * Commits all changes made to the Record since either creation, or the last commit operation.
12294      * <p>
12295      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12296      * of commit operations.
12297      */
12298     commit : function(){
12299         this.dirty = false;
12300         delete this.modified;
12301         this.editing = false;
12302         if(this.store){
12303             this.store.afterCommit(this);
12304         }
12305     },
12306
12307     // private
12308     hasError : function(){
12309         return this.error != null;
12310     },
12311
12312     // private
12313     clearError : function(){
12314         this.error = null;
12315     },
12316
12317     /**
12318      * Creates a copy of this record.
12319      * @param {String} id (optional) A new record id if you don't want to use this record's id
12320      * @return {Record}
12321      */
12322     copy : function(newId) {
12323         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12324     }
12325 };/*
12326  * Based on:
12327  * Ext JS Library 1.1.1
12328  * Copyright(c) 2006-2007, Ext JS, LLC.
12329  *
12330  * Originally Released Under LGPL - original licence link has changed is not relivant.
12331  *
12332  * Fork - LGPL
12333  * <script type="text/javascript">
12334  */
12335
12336
12337
12338 /**
12339  * @class Roo.data.Store
12340  * @extends Roo.util.Observable
12341  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12342  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12343  * <p>
12344  * 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
12345  * has no knowledge of the format of the data returned by the Proxy.<br>
12346  * <p>
12347  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12348  * instances from the data object. These records are cached and made available through accessor functions.
12349  * @constructor
12350  * Creates a new Store.
12351  * @param {Object} config A config object containing the objects needed for the Store to access data,
12352  * and read the data into Records.
12353  */
12354 Roo.data.Store = function(config){
12355     this.data = new Roo.util.MixedCollection(false);
12356     this.data.getKey = function(o){
12357         return o.id;
12358     };
12359     this.baseParams = {};
12360     // private
12361     this.paramNames = {
12362         "start" : "start",
12363         "limit" : "limit",
12364         "sort" : "sort",
12365         "dir" : "dir",
12366         "multisort" : "_multisort"
12367     };
12368
12369     if(config && config.data){
12370         this.inlineData = config.data;
12371         delete config.data;
12372     }
12373
12374     Roo.apply(this, config);
12375     
12376     if(this.reader){ // reader passed
12377         this.reader = Roo.factory(this.reader, Roo.data);
12378         this.reader.xmodule = this.xmodule || false;
12379         if(!this.recordType){
12380             this.recordType = this.reader.recordType;
12381         }
12382         if(this.reader.onMetaChange){
12383             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12384         }
12385     }
12386
12387     if(this.recordType){
12388         this.fields = this.recordType.prototype.fields;
12389     }
12390     this.modified = [];
12391
12392     this.addEvents({
12393         /**
12394          * @event datachanged
12395          * Fires when the data cache has changed, and a widget which is using this Store
12396          * as a Record cache should refresh its view.
12397          * @param {Store} this
12398          */
12399         datachanged : true,
12400         /**
12401          * @event metachange
12402          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12403          * @param {Store} this
12404          * @param {Object} meta The JSON metadata
12405          */
12406         metachange : true,
12407         /**
12408          * @event add
12409          * Fires when Records have been added to the Store
12410          * @param {Store} this
12411          * @param {Roo.data.Record[]} records The array of Records added
12412          * @param {Number} index The index at which the record(s) were added
12413          */
12414         add : true,
12415         /**
12416          * @event remove
12417          * Fires when a Record has been removed from the Store
12418          * @param {Store} this
12419          * @param {Roo.data.Record} record The Record that was removed
12420          * @param {Number} index The index at which the record was removed
12421          */
12422         remove : true,
12423         /**
12424          * @event update
12425          * Fires when a Record has been updated
12426          * @param {Store} this
12427          * @param {Roo.data.Record} record The Record that was updated
12428          * @param {String} operation The update operation being performed.  Value may be one of:
12429          * <pre><code>
12430  Roo.data.Record.EDIT
12431  Roo.data.Record.REJECT
12432  Roo.data.Record.COMMIT
12433          * </code></pre>
12434          */
12435         update : true,
12436         /**
12437          * @event clear
12438          * Fires when the data cache has been cleared.
12439          * @param {Store} this
12440          */
12441         clear : true,
12442         /**
12443          * @event beforeload
12444          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12445          * the load action will be canceled.
12446          * @param {Store} this
12447          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12448          */
12449         beforeload : true,
12450         /**
12451          * @event beforeloadadd
12452          * Fires after a new set of Records has been loaded.
12453          * @param {Store} this
12454          * @param {Roo.data.Record[]} records The Records that were loaded
12455          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12456          */
12457         beforeloadadd : true,
12458         /**
12459          * @event load
12460          * Fires after a new set of Records has been loaded, before they are added to the store.
12461          * @param {Store} this
12462          * @param {Roo.data.Record[]} records The Records that were loaded
12463          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12464          * @params {Object} return from reader
12465          */
12466         load : true,
12467         /**
12468          * @event loadexception
12469          * Fires if an exception occurs in the Proxy during loading.
12470          * Called with the signature of the Proxy's "loadexception" event.
12471          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12472          * 
12473          * @param {Proxy} 
12474          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12475          * @param {Object} load options 
12476          * @param {Object} jsonData from your request (normally this contains the Exception)
12477          */
12478         loadexception : true
12479     });
12480     
12481     if(this.proxy){
12482         this.proxy = Roo.factory(this.proxy, Roo.data);
12483         this.proxy.xmodule = this.xmodule || false;
12484         this.relayEvents(this.proxy,  ["loadexception"]);
12485     }
12486     this.sortToggle = {};
12487     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12488
12489     Roo.data.Store.superclass.constructor.call(this);
12490
12491     if(this.inlineData){
12492         this.loadData(this.inlineData);
12493         delete this.inlineData;
12494     }
12495 };
12496
12497 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12498      /**
12499     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12500     * without a remote query - used by combo/forms at present.
12501     */
12502     
12503     /**
12504     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12505     */
12506     /**
12507     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12508     */
12509     /**
12510     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12511     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12512     */
12513     /**
12514     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12515     * on any HTTP request
12516     */
12517     /**
12518     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12519     */
12520     /**
12521     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12522     */
12523     multiSort: false,
12524     /**
12525     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12526     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12527     */
12528     remoteSort : false,
12529
12530     /**
12531     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12532      * loaded or when a record is removed. (defaults to false).
12533     */
12534     pruneModifiedRecords : false,
12535
12536     // private
12537     lastOptions : null,
12538
12539     /**
12540      * Add Records to the Store and fires the add event.
12541      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12542      */
12543     add : function(records){
12544         records = [].concat(records);
12545         for(var i = 0, len = records.length; i < len; i++){
12546             records[i].join(this);
12547         }
12548         var index = this.data.length;
12549         this.data.addAll(records);
12550         this.fireEvent("add", this, records, index);
12551     },
12552
12553     /**
12554      * Remove a Record from the Store and fires the remove event.
12555      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12556      */
12557     remove : function(record){
12558         var index = this.data.indexOf(record);
12559         this.data.removeAt(index);
12560  
12561         if(this.pruneModifiedRecords){
12562             this.modified.remove(record);
12563         }
12564         this.fireEvent("remove", this, record, index);
12565     },
12566
12567     /**
12568      * Remove all Records from the Store and fires the clear event.
12569      */
12570     removeAll : function(){
12571         this.data.clear();
12572         if(this.pruneModifiedRecords){
12573             this.modified = [];
12574         }
12575         this.fireEvent("clear", this);
12576     },
12577
12578     /**
12579      * Inserts Records to the Store at the given index and fires the add event.
12580      * @param {Number} index The start index at which to insert the passed Records.
12581      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12582      */
12583     insert : function(index, records){
12584         records = [].concat(records);
12585         for(var i = 0, len = records.length; i < len; i++){
12586             this.data.insert(index, records[i]);
12587             records[i].join(this);
12588         }
12589         this.fireEvent("add", this, records, index);
12590     },
12591
12592     /**
12593      * Get the index within the cache of the passed Record.
12594      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12595      * @return {Number} The index of the passed Record. Returns -1 if not found.
12596      */
12597     indexOf : function(record){
12598         return this.data.indexOf(record);
12599     },
12600
12601     /**
12602      * Get the index within the cache of the Record with the passed id.
12603      * @param {String} id The id of the Record to find.
12604      * @return {Number} The index of the Record. Returns -1 if not found.
12605      */
12606     indexOfId : function(id){
12607         return this.data.indexOfKey(id);
12608     },
12609
12610     /**
12611      * Get the Record with the specified id.
12612      * @param {String} id The id of the Record to find.
12613      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12614      */
12615     getById : function(id){
12616         return this.data.key(id);
12617     },
12618
12619     /**
12620      * Get the Record at the specified index.
12621      * @param {Number} index The index of the Record to find.
12622      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12623      */
12624     getAt : function(index){
12625         return this.data.itemAt(index);
12626     },
12627
12628     /**
12629      * Returns a range of Records between specified indices.
12630      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12631      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12632      * @return {Roo.data.Record[]} An array of Records
12633      */
12634     getRange : function(start, end){
12635         return this.data.getRange(start, end);
12636     },
12637
12638     // private
12639     storeOptions : function(o){
12640         o = Roo.apply({}, o);
12641         delete o.callback;
12642         delete o.scope;
12643         this.lastOptions = o;
12644     },
12645
12646     /**
12647      * Loads the Record cache from the configured Proxy using the configured Reader.
12648      * <p>
12649      * If using remote paging, then the first load call must specify the <em>start</em>
12650      * and <em>limit</em> properties in the options.params property to establish the initial
12651      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12652      * <p>
12653      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12654      * and this call will return before the new data has been loaded. Perform any post-processing
12655      * in a callback function, or in a "load" event handler.</strong>
12656      * <p>
12657      * @param {Object} options An object containing properties which control loading options:<ul>
12658      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12659      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12660      * passed the following arguments:<ul>
12661      * <li>r : Roo.data.Record[]</li>
12662      * <li>options: Options object from the load call</li>
12663      * <li>success: Boolean success indicator</li></ul></li>
12664      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12665      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12666      * </ul>
12667      */
12668     load : function(options){
12669         options = options || {};
12670         if(this.fireEvent("beforeload", this, options) !== false){
12671             this.storeOptions(options);
12672             var p = Roo.apply(options.params || {}, this.baseParams);
12673             // if meta was not loaded from remote source.. try requesting it.
12674             if (!this.reader.metaFromRemote) {
12675                 p._requestMeta = 1;
12676             }
12677             if(this.sortInfo && this.remoteSort){
12678                 var pn = this.paramNames;
12679                 p[pn["sort"]] = this.sortInfo.field;
12680                 p[pn["dir"]] = this.sortInfo.direction;
12681             }
12682             if (this.multiSort) {
12683                 var pn = this.paramNames;
12684                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12685             }
12686             
12687             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12688         }
12689     },
12690
12691     /**
12692      * Reloads the Record cache from the configured Proxy using the configured Reader and
12693      * the options from the last load operation performed.
12694      * @param {Object} options (optional) An object containing properties which may override the options
12695      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12696      * the most recently used options are reused).
12697      */
12698     reload : function(options){
12699         this.load(Roo.applyIf(options||{}, this.lastOptions));
12700     },
12701
12702     // private
12703     // Called as a callback by the Reader during a load operation.
12704     loadRecords : function(o, options, success){
12705         if(!o || success === false){
12706             if(success !== false){
12707                 this.fireEvent("load", this, [], options, o);
12708             }
12709             if(options.callback){
12710                 options.callback.call(options.scope || this, [], options, false);
12711             }
12712             return;
12713         }
12714         // if data returned failure - throw an exception.
12715         if (o.success === false) {
12716             // show a message if no listener is registered.
12717             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12718                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12719             }
12720             // loadmask wil be hooked into this..
12721             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12722             return;
12723         }
12724         var r = o.records, t = o.totalRecords || r.length;
12725         
12726         this.fireEvent("beforeloadadd", this, r, options, o);
12727         
12728         if(!options || options.add !== true){
12729             if(this.pruneModifiedRecords){
12730                 this.modified = [];
12731             }
12732             for(var i = 0, len = r.length; i < len; i++){
12733                 r[i].join(this);
12734             }
12735             if(this.snapshot){
12736                 this.data = this.snapshot;
12737                 delete this.snapshot;
12738             }
12739             this.data.clear();
12740             this.data.addAll(r);
12741             this.totalLength = t;
12742             this.applySort();
12743             this.fireEvent("datachanged", this);
12744         }else{
12745             this.totalLength = Math.max(t, this.data.length+r.length);
12746             this.add(r);
12747         }
12748         
12749         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12750                 
12751             var e = new Roo.data.Record({});
12752
12753             e.set(this.parent.displayField, this.parent.emptyTitle);
12754             e.set(this.parent.valueField, '');
12755
12756             this.insert(0, e);
12757         }
12758             
12759         this.fireEvent("load", this, r, options, o);
12760         if(options.callback){
12761             options.callback.call(options.scope || this, r, options, true);
12762         }
12763     },
12764
12765
12766     /**
12767      * Loads data from a passed data block. A Reader which understands the format of the data
12768      * must have been configured in the constructor.
12769      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12770      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12771      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12772      */
12773     loadData : function(o, append){
12774         var r = this.reader.readRecords(o);
12775         this.loadRecords(r, {add: append}, true);
12776     },
12777     
12778      /**
12779      * using 'cn' the nested child reader read the child array into it's child stores.
12780      * @param {Object} rec The record with a 'children array
12781      */
12782     loadDataFromChildren : function(rec)
12783     {
12784         this.loadData(this.reader.toLoadData(rec));
12785     },
12786     
12787
12788     /**
12789      * Gets the number of cached records.
12790      * <p>
12791      * <em>If using paging, this may not be the total size of the dataset. If the data object
12792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12793      * the data set size</em>
12794      */
12795     getCount : function(){
12796         return this.data.length || 0;
12797     },
12798
12799     /**
12800      * Gets the total number of records in the dataset as returned by the server.
12801      * <p>
12802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12803      * the dataset size</em>
12804      */
12805     getTotalCount : function(){
12806         return this.totalLength || 0;
12807     },
12808
12809     /**
12810      * Returns the sort state of the Store as an object with two properties:
12811      * <pre><code>
12812  field {String} The name of the field by which the Records are sorted
12813  direction {String} The sort order, "ASC" or "DESC"
12814      * </code></pre>
12815      */
12816     getSortState : function(){
12817         return this.sortInfo;
12818     },
12819
12820     // private
12821     applySort : function(){
12822         if(this.sortInfo && !this.remoteSort){
12823             var s = this.sortInfo, f = s.field;
12824             var st = this.fields.get(f).sortType;
12825             var fn = function(r1, r2){
12826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12828             };
12829             this.data.sort(s.direction, fn);
12830             if(this.snapshot && this.snapshot != this.data){
12831                 this.snapshot.sort(s.direction, fn);
12832             }
12833         }
12834     },
12835
12836     /**
12837      * Sets the default sort column and order to be used by the next load operation.
12838      * @param {String} fieldName The name of the field to sort by.
12839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12840      */
12841     setDefaultSort : function(field, dir){
12842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12843     },
12844
12845     /**
12846      * Sort the Records.
12847      * If remote sorting is used, the sort is performed on the server, and the cache is
12848      * reloaded. If local sorting is used, the cache is sorted internally.
12849      * @param {String} fieldName The name of the field to sort by.
12850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12851      */
12852     sort : function(fieldName, dir){
12853         var f = this.fields.get(fieldName);
12854         if(!dir){
12855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12856             
12857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12859             }else{
12860                 dir = f.sortDir;
12861             }
12862         }
12863         this.sortToggle[f.name] = dir;
12864         this.sortInfo = {field: f.name, direction: dir};
12865         if(!this.remoteSort){
12866             this.applySort();
12867             this.fireEvent("datachanged", this);
12868         }else{
12869             this.load(this.lastOptions);
12870         }
12871     },
12872
12873     /**
12874      * Calls the specified function for each of the Records in the cache.
12875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12876      * Returning <em>false</em> aborts and exits the iteration.
12877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12878      */
12879     each : function(fn, scope){
12880         this.data.each(fn, scope);
12881     },
12882
12883     /**
12884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12885      * (e.g., during paging).
12886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12887      */
12888     getModifiedRecords : function(){
12889         return this.modified;
12890     },
12891
12892     // private
12893     createFilterFn : function(property, value, anyMatch){
12894         if(!value.exec){ // not a regex
12895             value = String(value);
12896             if(value.length == 0){
12897                 return false;
12898             }
12899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12900         }
12901         return function(r){
12902             return value.test(r.data[property]);
12903         };
12904     },
12905
12906     /**
12907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12908      * @param {String} property A field on your records
12909      * @param {Number} start The record index to start at (defaults to 0)
12910      * @param {Number} end The last record index to include (defaults to length - 1)
12911      * @return {Number} The sum
12912      */
12913     sum : function(property, start, end){
12914         var rs = this.data.items, v = 0;
12915         start = start || 0;
12916         end = (end || end === 0) ? end : rs.length-1;
12917
12918         for(var i = start; i <= end; i++){
12919             v += (rs[i].data[property] || 0);
12920         }
12921         return v;
12922     },
12923
12924     /**
12925      * Filter the records by a specified property.
12926      * @param {String} field A field on your records
12927      * @param {String/RegExp} value Either a string that the field
12928      * should start with or a RegExp to test against the field
12929      * @param {Boolean} anyMatch True to match any part not just the beginning
12930      */
12931     filter : function(property, value, anyMatch){
12932         var fn = this.createFilterFn(property, value, anyMatch);
12933         return fn ? this.filterBy(fn) : this.clearFilter();
12934     },
12935
12936     /**
12937      * Filter by a function. The specified function will be called with each
12938      * record in this data source. If the function returns true the record is included,
12939      * otherwise it is filtered.
12940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12941      * @param {Object} scope (optional) The scope of the function (defaults to this)
12942      */
12943     filterBy : function(fn, scope){
12944         this.snapshot = this.snapshot || this.data;
12945         this.data = this.queryBy(fn, scope||this);
12946         this.fireEvent("datachanged", this);
12947     },
12948
12949     /**
12950      * Query the records by a specified property.
12951      * @param {String} field A field on your records
12952      * @param {String/RegExp} value Either a string that the field
12953      * should start with or a RegExp to test against the field
12954      * @param {Boolean} anyMatch True to match any part not just the beginning
12955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12956      */
12957     query : function(property, value, anyMatch){
12958         var fn = this.createFilterFn(property, value, anyMatch);
12959         return fn ? this.queryBy(fn) : this.data.clone();
12960     },
12961
12962     /**
12963      * Query by a function. The specified function will be called with each
12964      * record in this data source. If the function returns true the record is included
12965      * in the results.
12966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12967      * @param {Object} scope (optional) The scope of the function (defaults to this)
12968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12969      **/
12970     queryBy : function(fn, scope){
12971         var data = this.snapshot || this.data;
12972         return data.filterBy(fn, scope||this);
12973     },
12974
12975     /**
12976      * Collects unique values for a particular dataIndex from this store.
12977      * @param {String} dataIndex The property to collect
12978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12980      * @return {Array} An array of the unique values
12981      **/
12982     collect : function(dataIndex, allowNull, bypassFilter){
12983         var d = (bypassFilter === true && this.snapshot) ?
12984                 this.snapshot.items : this.data.items;
12985         var v, sv, r = [], l = {};
12986         for(var i = 0, len = d.length; i < len; i++){
12987             v = d[i].data[dataIndex];
12988             sv = String(v);
12989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12990                 l[sv] = true;
12991                 r[r.length] = v;
12992             }
12993         }
12994         return r;
12995     },
12996
12997     /**
12998      * Revert to a view of the Record cache with no filtering applied.
12999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13000      */
13001     clearFilter : function(suppressEvent){
13002         if(this.snapshot && this.snapshot != this.data){
13003             this.data = this.snapshot;
13004             delete this.snapshot;
13005             if(suppressEvent !== true){
13006                 this.fireEvent("datachanged", this);
13007             }
13008         }
13009     },
13010
13011     // private
13012     afterEdit : function(record){
13013         if(this.modified.indexOf(record) == -1){
13014             this.modified.push(record);
13015         }
13016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13017     },
13018     
13019     // private
13020     afterReject : function(record){
13021         this.modified.remove(record);
13022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13023     },
13024
13025     // private
13026     afterCommit : function(record){
13027         this.modified.remove(record);
13028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13029     },
13030
13031     /**
13032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13034      */
13035     commitChanges : function(){
13036         var m = this.modified.slice(0);
13037         this.modified = [];
13038         for(var i = 0, len = m.length; i < len; i++){
13039             m[i].commit();
13040         }
13041     },
13042
13043     /**
13044      * Cancel outstanding changes on all changed records.
13045      */
13046     rejectChanges : function(){
13047         var m = this.modified.slice(0);
13048         this.modified = [];
13049         for(var i = 0, len = m.length; i < len; i++){
13050             m[i].reject();
13051         }
13052     },
13053
13054     onMetaChange : function(meta, rtype, o){
13055         this.recordType = rtype;
13056         this.fields = rtype.prototype.fields;
13057         delete this.snapshot;
13058         this.sortInfo = meta.sortInfo || this.sortInfo;
13059         this.modified = [];
13060         this.fireEvent('metachange', this, this.reader.meta);
13061     },
13062     
13063     moveIndex : function(data, type)
13064     {
13065         var index = this.indexOf(data);
13066         
13067         var newIndex = index + type;
13068         
13069         this.remove(data);
13070         
13071         this.insert(newIndex, data);
13072         
13073     }
13074 });/*
13075  * Based on:
13076  * Ext JS Library 1.1.1
13077  * Copyright(c) 2006-2007, Ext JS, LLC.
13078  *
13079  * Originally Released Under LGPL - original licence link has changed is not relivant.
13080  *
13081  * Fork - LGPL
13082  * <script type="text/javascript">
13083  */
13084
13085 /**
13086  * @class Roo.data.SimpleStore
13087  * @extends Roo.data.Store
13088  * Small helper class to make creating Stores from Array data easier.
13089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13090  * @cfg {Array} fields An array of field definition objects, or field name strings.
13091  * @cfg {Object} an existing reader (eg. copied from another store)
13092  * @cfg {Array} data The multi-dimensional array of data
13093  * @constructor
13094  * @param {Object} config
13095  */
13096 Roo.data.SimpleStore = function(config)
13097 {
13098     Roo.data.SimpleStore.superclass.constructor.call(this, {
13099         isLocal : true,
13100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13101                 id: config.id
13102             },
13103             Roo.data.Record.create(config.fields)
13104         ),
13105         proxy : new Roo.data.MemoryProxy(config.data)
13106     });
13107     this.load();
13108 };
13109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13110  * Based on:
13111  * Ext JS Library 1.1.1
13112  * Copyright(c) 2006-2007, Ext JS, LLC.
13113  *
13114  * Originally Released Under LGPL - original licence link has changed is not relivant.
13115  *
13116  * Fork - LGPL
13117  * <script type="text/javascript">
13118  */
13119
13120 /**
13121 /**
13122  * @extends Roo.data.Store
13123  * @class Roo.data.JsonStore
13124  * Small helper class to make creating Stores for JSON data easier. <br/>
13125 <pre><code>
13126 var store = new Roo.data.JsonStore({
13127     url: 'get-images.php',
13128     root: 'images',
13129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13130 });
13131 </code></pre>
13132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13133  * JsonReader and HttpProxy (unless inline data is provided).</b>
13134  * @cfg {Array} fields An array of field definition objects, or field name strings.
13135  * @constructor
13136  * @param {Object} config
13137  */
13138 Roo.data.JsonStore = function(c){
13139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13141         reader: new Roo.data.JsonReader(c, c.fields)
13142     }));
13143 };
13144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13145  * Based on:
13146  * Ext JS Library 1.1.1
13147  * Copyright(c) 2006-2007, Ext JS, LLC.
13148  *
13149  * Originally Released Under LGPL - original licence link has changed is not relivant.
13150  *
13151  * Fork - LGPL
13152  * <script type="text/javascript">
13153  */
13154
13155  
13156 Roo.data.Field = function(config){
13157     if(typeof config == "string"){
13158         config = {name: config};
13159     }
13160     Roo.apply(this, config);
13161     
13162     if(!this.type){
13163         this.type = "auto";
13164     }
13165     
13166     var st = Roo.data.SortTypes;
13167     // named sortTypes are supported, here we look them up
13168     if(typeof this.sortType == "string"){
13169         this.sortType = st[this.sortType];
13170     }
13171     
13172     // set default sortType for strings and dates
13173     if(!this.sortType){
13174         switch(this.type){
13175             case "string":
13176                 this.sortType = st.asUCString;
13177                 break;
13178             case "date":
13179                 this.sortType = st.asDate;
13180                 break;
13181             default:
13182                 this.sortType = st.none;
13183         }
13184     }
13185
13186     // define once
13187     var stripRe = /[\$,%]/g;
13188
13189     // prebuilt conversion function for this field, instead of
13190     // switching every time we're reading a value
13191     if(!this.convert){
13192         var cv, dateFormat = this.dateFormat;
13193         switch(this.type){
13194             case "":
13195             case "auto":
13196             case undefined:
13197                 cv = function(v){ return v; };
13198                 break;
13199             case "string":
13200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13201                 break;
13202             case "int":
13203                 cv = function(v){
13204                     return v !== undefined && v !== null && v !== '' ?
13205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13206                     };
13207                 break;
13208             case "float":
13209                 cv = function(v){
13210                     return v !== undefined && v !== null && v !== '' ?
13211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13212                     };
13213                 break;
13214             case "bool":
13215             case "boolean":
13216                 cv = function(v){ return v === true || v === "true" || v == 1; };
13217                 break;
13218             case "date":
13219                 cv = function(v){
13220                     if(!v){
13221                         return '';
13222                     }
13223                     if(v instanceof Date){
13224                         return v;
13225                     }
13226                     if(dateFormat){
13227                         if(dateFormat == "timestamp"){
13228                             return new Date(v*1000);
13229                         }
13230                         return Date.parseDate(v, dateFormat);
13231                     }
13232                     var parsed = Date.parse(v);
13233                     return parsed ? new Date(parsed) : null;
13234                 };
13235              break;
13236             
13237         }
13238         this.convert = cv;
13239     }
13240 };
13241
13242 Roo.data.Field.prototype = {
13243     dateFormat: null,
13244     defaultValue: "",
13245     mapping: null,
13246     sortType : null,
13247     sortDir : "ASC"
13248 };/*
13249  * Based on:
13250  * Ext JS Library 1.1.1
13251  * Copyright(c) 2006-2007, Ext JS, LLC.
13252  *
13253  * Originally Released Under LGPL - original licence link has changed is not relivant.
13254  *
13255  * Fork - LGPL
13256  * <script type="text/javascript">
13257  */
13258  
13259 // Base class for reading structured data from a data source.  This class is intended to be
13260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13261
13262 /**
13263  * @class Roo.data.DataReader
13264  * Base class for reading structured data from a data source.  This class is intended to be
13265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13266  */
13267
13268 Roo.data.DataReader = function(meta, recordType){
13269     
13270     this.meta = meta;
13271     
13272     this.recordType = recordType instanceof Array ? 
13273         Roo.data.Record.create(recordType) : recordType;
13274 };
13275
13276 Roo.data.DataReader.prototype = {
13277     
13278     
13279     readerType : 'Data',
13280      /**
13281      * Create an empty record
13282      * @param {Object} data (optional) - overlay some values
13283      * @return {Roo.data.Record} record created.
13284      */
13285     newRow :  function(d) {
13286         var da =  {};
13287         this.recordType.prototype.fields.each(function(c) {
13288             switch( c.type) {
13289                 case 'int' : da[c.name] = 0; break;
13290                 case 'date' : da[c.name] = new Date(); break;
13291                 case 'float' : da[c.name] = 0.0; break;
13292                 case 'boolean' : da[c.name] = false; break;
13293                 default : da[c.name] = ""; break;
13294             }
13295             
13296         });
13297         return new this.recordType(Roo.apply(da, d));
13298     }
13299     
13300     
13301 };/*
13302  * Based on:
13303  * Ext JS Library 1.1.1
13304  * Copyright(c) 2006-2007, Ext JS, LLC.
13305  *
13306  * Originally Released Under LGPL - original licence link has changed is not relivant.
13307  *
13308  * Fork - LGPL
13309  * <script type="text/javascript">
13310  */
13311
13312 /**
13313  * @class Roo.data.DataProxy
13314  * @extends Roo.data.Observable
13315  * This class is an abstract base class for implementations which provide retrieval of
13316  * unformatted data objects.<br>
13317  * <p>
13318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13319  * (of the appropriate type which knows how to parse the data object) to provide a block of
13320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13321  * <p>
13322  * Custom implementations must implement the load method as described in
13323  * {@link Roo.data.HttpProxy#load}.
13324  */
13325 Roo.data.DataProxy = function(){
13326     this.addEvents({
13327         /**
13328          * @event beforeload
13329          * Fires before a network request is made to retrieve a data object.
13330          * @param {Object} This DataProxy object.
13331          * @param {Object} params The params parameter to the load function.
13332          */
13333         beforeload : true,
13334         /**
13335          * @event load
13336          * Fires before the load method's callback is called.
13337          * @param {Object} This DataProxy object.
13338          * @param {Object} o The data object.
13339          * @param {Object} arg The callback argument object passed to the load function.
13340          */
13341         load : true,
13342         /**
13343          * @event loadexception
13344          * Fires if an Exception occurs during data retrieval.
13345          * @param {Object} This DataProxy object.
13346          * @param {Object} o The data object.
13347          * @param {Object} arg The callback argument object passed to the load function.
13348          * @param {Object} e The Exception.
13349          */
13350         loadexception : true
13351     });
13352     Roo.data.DataProxy.superclass.constructor.call(this);
13353 };
13354
13355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13356
13357     /**
13358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13359      */
13360 /*
13361  * Based on:
13362  * Ext JS Library 1.1.1
13363  * Copyright(c) 2006-2007, Ext JS, LLC.
13364  *
13365  * Originally Released Under LGPL - original licence link has changed is not relivant.
13366  *
13367  * Fork - LGPL
13368  * <script type="text/javascript">
13369  */
13370 /**
13371  * @class Roo.data.MemoryProxy
13372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13373  * to the Reader when its load method is called.
13374  * @constructor
13375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13376  */
13377 Roo.data.MemoryProxy = function(data){
13378     if (data.data) {
13379         data = data.data;
13380     }
13381     Roo.data.MemoryProxy.superclass.constructor.call(this);
13382     this.data = data;
13383 };
13384
13385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13386     
13387     /**
13388      * Load data from the requested source (in this case an in-memory
13389      * data object passed to the constructor), read the data object into
13390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13391      * process that block using the passed callback.
13392      * @param {Object} params This parameter is not used by the MemoryProxy class.
13393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13394      * object into a block of Roo.data.Records.
13395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13396      * The function must be passed <ul>
13397      * <li>The Record block object</li>
13398      * <li>The "arg" argument from the load function</li>
13399      * <li>A boolean success indicator</li>
13400      * </ul>
13401      * @param {Object} scope The scope in which to call the callback
13402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13403      */
13404     load : function(params, reader, callback, scope, arg){
13405         params = params || {};
13406         var result;
13407         try {
13408             result = reader.readRecords(params.data ? params.data :this.data);
13409         }catch(e){
13410             this.fireEvent("loadexception", this, arg, null, e);
13411             callback.call(scope, null, arg, false);
13412             return;
13413         }
13414         callback.call(scope, result, arg, true);
13415     },
13416     
13417     // private
13418     update : function(params, records){
13419         
13420     }
13421 });/*
13422  * Based on:
13423  * Ext JS Library 1.1.1
13424  * Copyright(c) 2006-2007, Ext JS, LLC.
13425  *
13426  * Originally Released Under LGPL - original licence link has changed is not relivant.
13427  *
13428  * Fork - LGPL
13429  * <script type="text/javascript">
13430  */
13431 /**
13432  * @class Roo.data.HttpProxy
13433  * @extends Roo.data.DataProxy
13434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13435  * configured to reference a certain URL.<br><br>
13436  * <p>
13437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13438  * from which the running page was served.<br><br>
13439  * <p>
13440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13441  * <p>
13442  * Be aware that to enable the browser to parse an XML document, the server must set
13443  * the Content-Type header in the HTTP response to "text/xml".
13444  * @constructor
13445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13447  * will be used to make the request.
13448  */
13449 Roo.data.HttpProxy = function(conn){
13450     Roo.data.HttpProxy.superclass.constructor.call(this);
13451     // is conn a conn config or a real conn?
13452     this.conn = conn;
13453     this.useAjax = !conn || !conn.events;
13454   
13455 };
13456
13457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13458     // thse are take from connection...
13459     
13460     /**
13461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13462      */
13463     /**
13464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13465      * extra parameters to each request made by this object. (defaults to undefined)
13466      */
13467     /**
13468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13469      *  to each request made by this object. (defaults to undefined)
13470      */
13471     /**
13472      * @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)
13473      */
13474     /**
13475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13476      */
13477      /**
13478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13479      * @type Boolean
13480      */
13481   
13482
13483     /**
13484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13485      * @type Boolean
13486      */
13487     /**
13488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13490      * a finer-grained basis than the DataProxy events.
13491      */
13492     getConnection : function(){
13493         return this.useAjax ? Roo.Ajax : this.conn;
13494     },
13495
13496     /**
13497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13499      * process that block using the passed callback.
13500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13501      * for the request to the remote server.
13502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13503      * object into a block of Roo.data.Records.
13504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13505      * The function must be passed <ul>
13506      * <li>The Record block object</li>
13507      * <li>The "arg" argument from the load function</li>
13508      * <li>A boolean success indicator</li>
13509      * </ul>
13510      * @param {Object} scope The scope in which to call the callback
13511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13512      */
13513     load : function(params, reader, callback, scope, arg){
13514         if(this.fireEvent("beforeload", this, params) !== false){
13515             var  o = {
13516                 params : params || {},
13517                 request: {
13518                     callback : callback,
13519                     scope : scope,
13520                     arg : arg
13521                 },
13522                 reader: reader,
13523                 callback : this.loadResponse,
13524                 scope: this
13525             };
13526             if(this.useAjax){
13527                 Roo.applyIf(o, this.conn);
13528                 if(this.activeRequest){
13529                     Roo.Ajax.abort(this.activeRequest);
13530                 }
13531                 this.activeRequest = Roo.Ajax.request(o);
13532             }else{
13533                 this.conn.request(o);
13534             }
13535         }else{
13536             callback.call(scope||this, null, arg, false);
13537         }
13538     },
13539
13540     // private
13541     loadResponse : function(o, success, response){
13542         delete this.activeRequest;
13543         if(!success){
13544             this.fireEvent("loadexception", this, o, response);
13545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13546             return;
13547         }
13548         var result;
13549         try {
13550             result = o.reader.read(response);
13551         }catch(e){
13552             this.fireEvent("loadexception", this, o, response, e);
13553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13554             return;
13555         }
13556         
13557         this.fireEvent("load", this, o, o.request.arg);
13558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13559     },
13560
13561     // private
13562     update : function(dataSet){
13563
13564     },
13565
13566     // private
13567     updateResponse : function(dataSet){
13568
13569     }
13570 });/*
13571  * Based on:
13572  * Ext JS Library 1.1.1
13573  * Copyright(c) 2006-2007, Ext JS, LLC.
13574  *
13575  * Originally Released Under LGPL - original licence link has changed is not relivant.
13576  *
13577  * Fork - LGPL
13578  * <script type="text/javascript">
13579  */
13580
13581 /**
13582  * @class Roo.data.ScriptTagProxy
13583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13584  * other than the originating domain of the running page.<br><br>
13585  * <p>
13586  * <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
13587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13588  * <p>
13589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13590  * source code that is used as the source inside a &lt;script> tag.<br><br>
13591  * <p>
13592  * In order for the browser to process the returned data, the server must wrap the data object
13593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13595  * depending on whether the callback name was passed:
13596  * <p>
13597  * <pre><code>
13598 boolean scriptTag = false;
13599 String cb = request.getParameter("callback");
13600 if (cb != null) {
13601     scriptTag = true;
13602     response.setContentType("text/javascript");
13603 } else {
13604     response.setContentType("application/x-json");
13605 }
13606 Writer out = response.getWriter();
13607 if (scriptTag) {
13608     out.write(cb + "(");
13609 }
13610 out.print(dataBlock.toJsonString());
13611 if (scriptTag) {
13612     out.write(");");
13613 }
13614 </pre></code>
13615  *
13616  * @constructor
13617  * @param {Object} config A configuration object.
13618  */
13619 Roo.data.ScriptTagProxy = function(config){
13620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13621     Roo.apply(this, config);
13622     this.head = document.getElementsByTagName("head")[0];
13623 };
13624
13625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13626
13627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13628     /**
13629      * @cfg {String} url The URL from which to request the data object.
13630      */
13631     /**
13632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13633      */
13634     timeout : 30000,
13635     /**
13636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13637      * the server the name of the callback function set up by the load call to process the returned data object.
13638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13639      * javascript output which calls this named function passing the data object as its only parameter.
13640      */
13641     callbackParam : "callback",
13642     /**
13643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13644      * name to the request.
13645      */
13646     nocache : true,
13647
13648     /**
13649      * Load data from the configured URL, read the data object into
13650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13651      * process that block using the passed callback.
13652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13653      * for the request to the remote server.
13654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13655      * object into a block of Roo.data.Records.
13656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13657      * The function must be passed <ul>
13658      * <li>The Record block object</li>
13659      * <li>The "arg" argument from the load function</li>
13660      * <li>A boolean success indicator</li>
13661      * </ul>
13662      * @param {Object} scope The scope in which to call the callback
13663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13664      */
13665     load : function(params, reader, callback, scope, arg){
13666         if(this.fireEvent("beforeload", this, params) !== false){
13667
13668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13669
13670             var url = this.url;
13671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13672             if(this.nocache){
13673                 url += "&_dc=" + (new Date().getTime());
13674             }
13675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13676             var trans = {
13677                 id : transId,
13678                 cb : "stcCallback"+transId,
13679                 scriptId : "stcScript"+transId,
13680                 params : params,
13681                 arg : arg,
13682                 url : url,
13683                 callback : callback,
13684                 scope : scope,
13685                 reader : reader
13686             };
13687             var conn = this;
13688
13689             window[trans.cb] = function(o){
13690                 conn.handleResponse(o, trans);
13691             };
13692
13693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13694
13695             if(this.autoAbort !== false){
13696                 this.abort();
13697             }
13698
13699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13700
13701             var script = document.createElement("script");
13702             script.setAttribute("src", url);
13703             script.setAttribute("type", "text/javascript");
13704             script.setAttribute("id", trans.scriptId);
13705             this.head.appendChild(script);
13706
13707             this.trans = trans;
13708         }else{
13709             callback.call(scope||this, null, arg, false);
13710         }
13711     },
13712
13713     // private
13714     isLoading : function(){
13715         return this.trans ? true : false;
13716     },
13717
13718     /**
13719      * Abort the current server request.
13720      */
13721     abort : function(){
13722         if(this.isLoading()){
13723             this.destroyTrans(this.trans);
13724         }
13725     },
13726
13727     // private
13728     destroyTrans : function(trans, isLoaded){
13729         this.head.removeChild(document.getElementById(trans.scriptId));
13730         clearTimeout(trans.timeoutId);
13731         if(isLoaded){
13732             window[trans.cb] = undefined;
13733             try{
13734                 delete window[trans.cb];
13735             }catch(e){}
13736         }else{
13737             // if hasn't been loaded, wait for load to remove it to prevent script error
13738             window[trans.cb] = function(){
13739                 window[trans.cb] = undefined;
13740                 try{
13741                     delete window[trans.cb];
13742                 }catch(e){}
13743             };
13744         }
13745     },
13746
13747     // private
13748     handleResponse : function(o, trans){
13749         this.trans = false;
13750         this.destroyTrans(trans, true);
13751         var result;
13752         try {
13753             result = trans.reader.readRecords(o);
13754         }catch(e){
13755             this.fireEvent("loadexception", this, o, trans.arg, e);
13756             trans.callback.call(trans.scope||window, null, trans.arg, false);
13757             return;
13758         }
13759         this.fireEvent("load", this, o, trans.arg);
13760         trans.callback.call(trans.scope||window, result, trans.arg, true);
13761     },
13762
13763     // private
13764     handleFailure : function(trans){
13765         this.trans = false;
13766         this.destroyTrans(trans, false);
13767         this.fireEvent("loadexception", this, null, trans.arg);
13768         trans.callback.call(trans.scope||window, null, trans.arg, false);
13769     }
13770 });/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780
13781 /**
13782  * @class Roo.data.JsonReader
13783  * @extends Roo.data.DataReader
13784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13785  * based on mappings in a provided Roo.data.Record constructor.
13786  * 
13787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13788  * in the reply previously. 
13789  * 
13790  * <p>
13791  * Example code:
13792  * <pre><code>
13793 var RecordDef = Roo.data.Record.create([
13794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13796 ]);
13797 var myReader = new Roo.data.JsonReader({
13798     totalProperty: "results",    // The property which contains the total dataset size (optional)
13799     root: "rows",                // The property which contains an Array of row objects
13800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13801 }, RecordDef);
13802 </code></pre>
13803  * <p>
13804  * This would consume a JSON file like this:
13805  * <pre><code>
13806 { 'results': 2, 'rows': [
13807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13809 }
13810 </code></pre>
13811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13813  * paged from the remote server.
13814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13815  * @cfg {String} root name of the property which contains the Array of row objects.
13816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13817  * @cfg {Array} fields Array of field definition objects
13818  * @constructor
13819  * Create a new JsonReader
13820  * @param {Object} meta Metadata configuration options
13821  * @param {Object} recordType Either an Array of field definition objects,
13822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13823  */
13824 Roo.data.JsonReader = function(meta, recordType){
13825     
13826     meta = meta || {};
13827     // set some defaults:
13828     Roo.applyIf(meta, {
13829         totalProperty: 'total',
13830         successProperty : 'success',
13831         root : 'data',
13832         id : 'id'
13833     });
13834     
13835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13836 };
13837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13838     
13839     readerType : 'Json',
13840     
13841     /**
13842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13843      * Used by Store query builder to append _requestMeta to params.
13844      * 
13845      */
13846     metaFromRemote : false,
13847     /**
13848      * This method is only used by a DataProxy which has retrieved data from a remote server.
13849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13850      * @return {Object} data A data block which is used by an Roo.data.Store object as
13851      * a cache of Roo.data.Records.
13852      */
13853     read : function(response){
13854         var json = response.responseText;
13855        
13856         var o = /* eval:var:o */ eval("("+json+")");
13857         if(!o) {
13858             throw {message: "JsonReader.read: Json object not found"};
13859         }
13860         
13861         if(o.metaData){
13862             
13863             delete this.ef;
13864             this.metaFromRemote = true;
13865             this.meta = o.metaData;
13866             this.recordType = Roo.data.Record.create(o.metaData.fields);
13867             this.onMetaChange(this.meta, this.recordType, o);
13868         }
13869         return this.readRecords(o);
13870     },
13871
13872     // private function a store will implement
13873     onMetaChange : function(meta, recordType, o){
13874
13875     },
13876
13877     /**
13878          * @ignore
13879          */
13880     simpleAccess: function(obj, subsc) {
13881         return obj[subsc];
13882     },
13883
13884         /**
13885          * @ignore
13886          */
13887     getJsonAccessor: function(){
13888         var re = /[\[\.]/;
13889         return function(expr) {
13890             try {
13891                 return(re.test(expr))
13892                     ? new Function("obj", "return obj." + expr)
13893                     : function(obj){
13894                         return obj[expr];
13895                     };
13896             } catch(e){}
13897             return Roo.emptyFn;
13898         };
13899     }(),
13900
13901     /**
13902      * Create a data block containing Roo.data.Records from an XML document.
13903      * @param {Object} o An object which contains an Array of row objects in the property specified
13904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13905      * which contains the total size of the dataset.
13906      * @return {Object} data A data block which is used by an Roo.data.Store object as
13907      * a cache of Roo.data.Records.
13908      */
13909     readRecords : function(o){
13910         /**
13911          * After any data loads, the raw JSON data is available for further custom processing.
13912          * @type Object
13913          */
13914         this.o = o;
13915         var s = this.meta, Record = this.recordType,
13916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13917
13918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13919         if (!this.ef) {
13920             if(s.totalProperty) {
13921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13922                 }
13923                 if(s.successProperty) {
13924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13925                 }
13926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13927                 if (s.id) {
13928                         var g = this.getJsonAccessor(s.id);
13929                         this.getId = function(rec) {
13930                                 var r = g(rec);  
13931                                 return (r === undefined || r === "") ? null : r;
13932                         };
13933                 } else {
13934                         this.getId = function(){return null;};
13935                 }
13936             this.ef = [];
13937             for(var jj = 0; jj < fl; jj++){
13938                 f = fi[jj];
13939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13940                 this.ef[jj] = this.getJsonAccessor(map);
13941             }
13942         }
13943
13944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13945         if(s.totalProperty){
13946             var vt = parseInt(this.getTotal(o), 10);
13947             if(!isNaN(vt)){
13948                 totalRecords = vt;
13949             }
13950         }
13951         if(s.successProperty){
13952             var vs = this.getSuccess(o);
13953             if(vs === false || vs === 'false'){
13954                 success = false;
13955             }
13956         }
13957         var records = [];
13958         for(var i = 0; i < c; i++){
13959                 var n = root[i];
13960             var values = {};
13961             var id = this.getId(n);
13962             for(var j = 0; j < fl; j++){
13963                 f = fi[j];
13964             var v = this.ef[j](n);
13965             if (!f.convert) {
13966                 Roo.log('missing convert for ' + f.name);
13967                 Roo.log(f);
13968                 continue;
13969             }
13970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13971             }
13972             var record = new Record(values, id);
13973             record.json = n;
13974             records[i] = record;
13975         }
13976         return {
13977             raw : o,
13978             success : success,
13979             records : records,
13980             totalRecords : totalRecords
13981         };
13982     },
13983     // used when loading children.. @see loadDataFromChildren
13984     toLoadData: function(rec)
13985     {
13986         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13987         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13988         return { data : data, total : data.length };
13989         
13990     }
13991 });/*
13992  * Based on:
13993  * Ext JS Library 1.1.1
13994  * Copyright(c) 2006-2007, Ext JS, LLC.
13995  *
13996  * Originally Released Under LGPL - original licence link has changed is not relivant.
13997  *
13998  * Fork - LGPL
13999  * <script type="text/javascript">
14000  */
14001
14002 /**
14003  * @class Roo.data.ArrayReader
14004  * @extends Roo.data.DataReader
14005  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14006  * Each element of that Array represents a row of data fields. The
14007  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14008  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14009  * <p>
14010  * Example code:.
14011  * <pre><code>
14012 var RecordDef = Roo.data.Record.create([
14013     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14014     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14015 ]);
14016 var myReader = new Roo.data.ArrayReader({
14017     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14018 }, RecordDef);
14019 </code></pre>
14020  * <p>
14021  * This would consume an Array like this:
14022  * <pre><code>
14023 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14024   </code></pre>
14025  
14026  * @constructor
14027  * Create a new JsonReader
14028  * @param {Object} meta Metadata configuration options.
14029  * @param {Object|Array} recordType Either an Array of field definition objects
14030  * 
14031  * @cfg {Array} fields Array of field definition objects
14032  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14033  * as specified to {@link Roo.data.Record#create},
14034  * or an {@link Roo.data.Record} object
14035  *
14036  * 
14037  * created using {@link Roo.data.Record#create}.
14038  */
14039 Roo.data.ArrayReader = function(meta, recordType)
14040 {    
14041     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14042 };
14043
14044 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14045     
14046       /**
14047      * Create a data block containing Roo.data.Records from an XML document.
14048      * @param {Object} o An Array of row objects which represents the dataset.
14049      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14050      * a cache of Roo.data.Records.
14051      */
14052     readRecords : function(o)
14053     {
14054         var sid = this.meta ? this.meta.id : null;
14055         var recordType = this.recordType, fields = recordType.prototype.fields;
14056         var records = [];
14057         var root = o;
14058         for(var i = 0; i < root.length; i++){
14059                 var n = root[i];
14060             var values = {};
14061             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14062             for(var j = 0, jlen = fields.length; j < jlen; j++){
14063                 var f = fields.items[j];
14064                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14065                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14066                 v = f.convert(v);
14067                 values[f.name] = v;
14068             }
14069             var record = new recordType(values, id);
14070             record.json = n;
14071             records[records.length] = record;
14072         }
14073         return {
14074             records : records,
14075             totalRecords : records.length
14076         };
14077     },
14078     // used when loading children.. @see loadDataFromChildren
14079     toLoadData: function(rec)
14080     {
14081         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14082         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14083         
14084     }
14085     
14086     
14087 });/*
14088  * - LGPL
14089  * * 
14090  */
14091
14092 /**
14093  * @class Roo.bootstrap.ComboBox
14094  * @extends Roo.bootstrap.TriggerField
14095  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14096  * @cfg {Boolean} append (true|false) default false
14097  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14098  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14099  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14100  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14101  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14102  * @cfg {Boolean} animate default true
14103  * @cfg {Boolean} emptyResultText only for touch device
14104  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14105  * @cfg {String} emptyTitle default ''
14106  * @constructor
14107  * Create a new ComboBox.
14108  * @param {Object} config Configuration options
14109  */
14110 Roo.bootstrap.ComboBox = function(config){
14111     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14112     this.addEvents({
14113         /**
14114          * @event expand
14115          * Fires when the dropdown list is expanded
14116         * @param {Roo.bootstrap.ComboBox} combo This combo box
14117         */
14118         'expand' : true,
14119         /**
14120          * @event collapse
14121          * Fires when the dropdown list is collapsed
14122         * @param {Roo.bootstrap.ComboBox} combo This combo box
14123         */
14124         'collapse' : true,
14125         /**
14126          * @event beforeselect
14127          * Fires before a list item is selected. Return false to cancel the selection.
14128         * @param {Roo.bootstrap.ComboBox} combo This combo box
14129         * @param {Roo.data.Record} record The data record returned from the underlying store
14130         * @param {Number} index The index of the selected item in the dropdown list
14131         */
14132         'beforeselect' : true,
14133         /**
14134          * @event select
14135          * Fires when a list item is selected
14136         * @param {Roo.bootstrap.ComboBox} combo This combo box
14137         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14138         * @param {Number} index The index of the selected item in the dropdown list
14139         */
14140         'select' : true,
14141         /**
14142          * @event beforequery
14143          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14144          * The event object passed has these properties:
14145         * @param {Roo.bootstrap.ComboBox} combo This combo box
14146         * @param {String} query The query
14147         * @param {Boolean} forceAll true to force "all" query
14148         * @param {Boolean} cancel true to cancel the query
14149         * @param {Object} e The query event object
14150         */
14151         'beforequery': true,
14152          /**
14153          * @event add
14154          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14155         * @param {Roo.bootstrap.ComboBox} combo This combo box
14156         */
14157         'add' : true,
14158         /**
14159          * @event edit
14160          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14161         * @param {Roo.bootstrap.ComboBox} combo This combo box
14162         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14163         */
14164         'edit' : true,
14165         /**
14166          * @event remove
14167          * Fires when the remove value from the combobox array
14168         * @param {Roo.bootstrap.ComboBox} combo This combo box
14169         */
14170         'remove' : true,
14171         /**
14172          * @event afterremove
14173          * Fires when the remove value from the combobox array
14174         * @param {Roo.bootstrap.ComboBox} combo This combo box
14175         */
14176         'afterremove' : true,
14177         /**
14178          * @event specialfilter
14179          * Fires when specialfilter
14180             * @param {Roo.bootstrap.ComboBox} combo This combo box
14181             */
14182         'specialfilter' : true,
14183         /**
14184          * @event tick
14185          * Fires when tick the element
14186             * @param {Roo.bootstrap.ComboBox} combo This combo box
14187             */
14188         'tick' : true,
14189         /**
14190          * @event touchviewdisplay
14191          * Fires when touch view require special display (default is using displayField)
14192             * @param {Roo.bootstrap.ComboBox} combo This combo box
14193             * @param {Object} cfg set html .
14194             */
14195         'touchviewdisplay' : true
14196         
14197     });
14198     
14199     this.item = [];
14200     this.tickItems = [];
14201     
14202     this.selectedIndex = -1;
14203     if(this.mode == 'local'){
14204         if(config.queryDelay === undefined){
14205             this.queryDelay = 10;
14206         }
14207         if(config.minChars === undefined){
14208             this.minChars = 0;
14209         }
14210     }
14211 };
14212
14213 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14214      
14215     /**
14216      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14217      * rendering into an Roo.Editor, defaults to false)
14218      */
14219     /**
14220      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14221      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14222      */
14223     /**
14224      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14225      */
14226     /**
14227      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14228      * the dropdown list (defaults to undefined, with no header element)
14229      */
14230
14231      /**
14232      * @cfg {String/Roo.Template} tpl The template to use to render the output
14233      */
14234      
14235      /**
14236      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14237      */
14238     listWidth: undefined,
14239     /**
14240      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14241      * mode = 'remote' or 'text' if mode = 'local')
14242      */
14243     displayField: undefined,
14244     
14245     /**
14246      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14247      * mode = 'remote' or 'value' if mode = 'local'). 
14248      * Note: use of a valueField requires the user make a selection
14249      * in order for a value to be mapped.
14250      */
14251     valueField: undefined,
14252     /**
14253      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14254      */
14255     modalTitle : '',
14256     
14257     /**
14258      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14259      * field's data value (defaults to the underlying DOM element's name)
14260      */
14261     hiddenName: undefined,
14262     /**
14263      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14264      */
14265     listClass: '',
14266     /**
14267      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14268      */
14269     selectedClass: 'active',
14270     
14271     /**
14272      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14273      */
14274     shadow:'sides',
14275     /**
14276      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14277      * anchor positions (defaults to 'tl-bl')
14278      */
14279     listAlign: 'tl-bl?',
14280     /**
14281      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14282      */
14283     maxHeight: 300,
14284     /**
14285      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14286      * query specified by the allQuery config option (defaults to 'query')
14287      */
14288     triggerAction: 'query',
14289     /**
14290      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14291      * (defaults to 4, does not apply if editable = false)
14292      */
14293     minChars : 4,
14294     /**
14295      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14296      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14297      */
14298     typeAhead: false,
14299     /**
14300      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14301      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14302      */
14303     queryDelay: 500,
14304     /**
14305      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14306      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14307      */
14308     pageSize: 0,
14309     /**
14310      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14311      * when editable = true (defaults to false)
14312      */
14313     selectOnFocus:false,
14314     /**
14315      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14316      */
14317     queryParam: 'query',
14318     /**
14319      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14320      * when mode = 'remote' (defaults to 'Loading...')
14321      */
14322     loadingText: 'Loading...',
14323     /**
14324      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14325      */
14326     resizable: false,
14327     /**
14328      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14329      */
14330     handleHeight : 8,
14331     /**
14332      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14333      * traditional select (defaults to true)
14334      */
14335     editable: true,
14336     /**
14337      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14338      */
14339     allQuery: '',
14340     /**
14341      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14342      */
14343     mode: 'remote',
14344     /**
14345      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14346      * listWidth has a higher value)
14347      */
14348     minListWidth : 70,
14349     /**
14350      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14351      * allow the user to set arbitrary text into the field (defaults to false)
14352      */
14353     forceSelection:false,
14354     /**
14355      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14356      * if typeAhead = true (defaults to 250)
14357      */
14358     typeAheadDelay : 250,
14359     /**
14360      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14361      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14362      */
14363     valueNotFoundText : undefined,
14364     /**
14365      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14366      */
14367     blockFocus : false,
14368     
14369     /**
14370      * @cfg {Boolean} disableClear Disable showing of clear button.
14371      */
14372     disableClear : false,
14373     /**
14374      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14375      */
14376     alwaysQuery : false,
14377     
14378     /**
14379      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14380      */
14381     multiple : false,
14382     
14383     /**
14384      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14385      */
14386     invalidClass : "has-warning",
14387     
14388     /**
14389      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14390      */
14391     validClass : "has-success",
14392     
14393     /**
14394      * @cfg {Boolean} specialFilter (true|false) special filter default false
14395      */
14396     specialFilter : false,
14397     
14398     /**
14399      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14400      */
14401     mobileTouchView : true,
14402     
14403     /**
14404      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14405      */
14406     useNativeIOS : false,
14407     
14408     /**
14409      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14410      */
14411     mobile_restrict_height : false,
14412     
14413     ios_options : false,
14414     
14415     //private
14416     addicon : false,
14417     editicon: false,
14418     
14419     page: 0,
14420     hasQuery: false,
14421     append: false,
14422     loadNext: false,
14423     autoFocus : true,
14424     tickable : false,
14425     btnPosition : 'right',
14426     triggerList : true,
14427     showToggleBtn : true,
14428     animate : true,
14429     emptyResultText: 'Empty',
14430     triggerText : 'Select',
14431     emptyTitle : '',
14432     
14433     // element that contains real text value.. (when hidden is used..)
14434     
14435     getAutoCreate : function()
14436     {   
14437         var cfg = false;
14438         //render
14439         /*
14440          * Render classic select for iso
14441          */
14442         
14443         if(Roo.isIOS && this.useNativeIOS){
14444             cfg = this.getAutoCreateNativeIOS();
14445             return cfg;
14446         }
14447         
14448         /*
14449          * Touch Devices
14450          */
14451         
14452         if(Roo.isTouch && this.mobileTouchView){
14453             cfg = this.getAutoCreateTouchView();
14454             return cfg;;
14455         }
14456         
14457         /*
14458          *  Normal ComboBox
14459          */
14460         if(!this.tickable){
14461             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14462             return cfg;
14463         }
14464         
14465         /*
14466          *  ComboBox with tickable selections
14467          */
14468              
14469         var align = this.labelAlign || this.parentLabelAlign();
14470         
14471         cfg = {
14472             cls : 'form-group roo-combobox-tickable' //input-group
14473         };
14474         
14475         var btn_text_select = '';
14476         var btn_text_done = '';
14477         var btn_text_cancel = '';
14478         
14479         if (this.btn_text_show) {
14480             btn_text_select = 'Select';
14481             btn_text_done = 'Done';
14482             btn_text_cancel = 'Cancel'; 
14483         }
14484         
14485         var buttons = {
14486             tag : 'div',
14487             cls : 'tickable-buttons',
14488             cn : [
14489                 {
14490                     tag : 'button',
14491                     type : 'button',
14492                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14493                     //html : this.triggerText
14494                     html: btn_text_select
14495                 },
14496                 {
14497                     tag : 'button',
14498                     type : 'button',
14499                     name : 'ok',
14500                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14501                     //html : 'Done'
14502                     html: btn_text_done
14503                 },
14504                 {
14505                     tag : 'button',
14506                     type : 'button',
14507                     name : 'cancel',
14508                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14509                     //html : 'Cancel'
14510                     html: btn_text_cancel
14511                 }
14512             ]
14513         };
14514         
14515         if(this.editable){
14516             buttons.cn.unshift({
14517                 tag: 'input',
14518                 cls: 'roo-select2-search-field-input'
14519             });
14520         }
14521         
14522         var _this = this;
14523         
14524         Roo.each(buttons.cn, function(c){
14525             if (_this.size) {
14526                 c.cls += ' btn-' + _this.size;
14527             }
14528
14529             if (_this.disabled) {
14530                 c.disabled = true;
14531             }
14532         });
14533         
14534         var box = {
14535             tag: 'div',
14536             style : 'display: contents',
14537             cn: [
14538                 {
14539                     tag: 'input',
14540                     type : 'hidden',
14541                     cls: 'form-hidden-field'
14542                 },
14543                 {
14544                     tag: 'ul',
14545                     cls: 'roo-select2-choices',
14546                     cn:[
14547                         {
14548                             tag: 'li',
14549                             cls: 'roo-select2-search-field',
14550                             cn: [
14551                                 buttons
14552                             ]
14553                         }
14554                     ]
14555                 }
14556             ]
14557         };
14558         
14559         var combobox = {
14560             cls: 'roo-select2-container input-group roo-select2-container-multi',
14561             cn: [
14562                 
14563                 box
14564 //                {
14565 //                    tag: 'ul',
14566 //                    cls: 'typeahead typeahead-long dropdown-menu',
14567 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14568 //                }
14569             ]
14570         };
14571         
14572         if(this.hasFeedback && !this.allowBlank){
14573             
14574             var feedback = {
14575                 tag: 'span',
14576                 cls: 'glyphicon form-control-feedback'
14577             };
14578
14579             combobox.cn.push(feedback);
14580         }
14581         
14582         var indicator = {
14583             tag : 'i',
14584             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14585             tooltip : 'This field is required'
14586         };
14587         if (Roo.bootstrap.version == 4) {
14588             indicator = {
14589                 tag : 'i',
14590                 style : 'display:none'
14591             };
14592         }
14593         if (align ==='left' && this.fieldLabel.length) {
14594             
14595             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14596             
14597             cfg.cn = [
14598                 indicator,
14599                 {
14600                     tag: 'label',
14601                     'for' :  id,
14602                     cls : 'control-label col-form-label',
14603                     html : this.fieldLabel
14604
14605                 },
14606                 {
14607                     cls : "", 
14608                     cn: [
14609                         combobox
14610                     ]
14611                 }
14612
14613             ];
14614             
14615             var labelCfg = cfg.cn[1];
14616             var contentCfg = cfg.cn[2];
14617             
14618
14619             if(this.indicatorpos == 'right'){
14620                 
14621                 cfg.cn = [
14622                     {
14623                         tag: 'label',
14624                         'for' :  id,
14625                         cls : 'control-label col-form-label',
14626                         cn : [
14627                             {
14628                                 tag : 'span',
14629                                 html : this.fieldLabel
14630                             },
14631                             indicator
14632                         ]
14633                     },
14634                     {
14635                         cls : "",
14636                         cn: [
14637                             combobox
14638                         ]
14639                     }
14640
14641                 ];
14642                 
14643                 
14644                 
14645                 labelCfg = cfg.cn[0];
14646                 contentCfg = cfg.cn[1];
14647             
14648             }
14649             
14650             if(this.labelWidth > 12){
14651                 labelCfg.style = "width: " + this.labelWidth + 'px';
14652             }
14653             
14654             if(this.labelWidth < 13 && this.labelmd == 0){
14655                 this.labelmd = this.labelWidth;
14656             }
14657             
14658             if(this.labellg > 0){
14659                 labelCfg.cls += ' col-lg-' + this.labellg;
14660                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14661             }
14662             
14663             if(this.labelmd > 0){
14664                 labelCfg.cls += ' col-md-' + this.labelmd;
14665                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14666             }
14667             
14668             if(this.labelsm > 0){
14669                 labelCfg.cls += ' col-sm-' + this.labelsm;
14670                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14671             }
14672             
14673             if(this.labelxs > 0){
14674                 labelCfg.cls += ' col-xs-' + this.labelxs;
14675                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14676             }
14677                 
14678                 
14679         } else if ( this.fieldLabel.length) {
14680 //                Roo.log(" label");
14681                  cfg.cn = [
14682                    indicator,
14683                     {
14684                         tag: 'label',
14685                         //cls : 'input-group-addon',
14686                         html : this.fieldLabel
14687                     },
14688                     combobox
14689                 ];
14690                 
14691                 if(this.indicatorpos == 'right'){
14692                     cfg.cn = [
14693                         {
14694                             tag: 'label',
14695                             //cls : 'input-group-addon',
14696                             html : this.fieldLabel
14697                         },
14698                         indicator,
14699                         combobox
14700                     ];
14701                     
14702                 }
14703
14704         } else {
14705             
14706 //                Roo.log(" no label && no align");
14707                 cfg = combobox
14708                      
14709                 
14710         }
14711          
14712         var settings=this;
14713         ['xs','sm','md','lg'].map(function(size){
14714             if (settings[size]) {
14715                 cfg.cls += ' col-' + size + '-' + settings[size];
14716             }
14717         });
14718         
14719         return cfg;
14720         
14721     },
14722     
14723     _initEventsCalled : false,
14724     
14725     // private
14726     initEvents: function()
14727     {   
14728         if (this._initEventsCalled) { // as we call render... prevent looping...
14729             return;
14730         }
14731         this._initEventsCalled = true;
14732         
14733         if (!this.store) {
14734             throw "can not find store for combo";
14735         }
14736         
14737         this.indicator = this.indicatorEl();
14738         
14739         this.store = Roo.factory(this.store, Roo.data);
14740         this.store.parent = this;
14741         
14742         // if we are building from html. then this element is so complex, that we can not really
14743         // use the rendered HTML.
14744         // so we have to trash and replace the previous code.
14745         if (Roo.XComponent.build_from_html) {
14746             // remove this element....
14747             var e = this.el.dom, k=0;
14748             while (e ) { e = e.previousSibling;  ++k;}
14749
14750             this.el.remove();
14751             
14752             this.el=false;
14753             this.rendered = false;
14754             
14755             this.render(this.parent().getChildContainer(true), k);
14756         }
14757         
14758         if(Roo.isIOS && this.useNativeIOS){
14759             this.initIOSView();
14760             return;
14761         }
14762         
14763         /*
14764          * Touch Devices
14765          */
14766         
14767         if(Roo.isTouch && this.mobileTouchView){
14768             this.initTouchView();
14769             return;
14770         }
14771         
14772         if(this.tickable){
14773             this.initTickableEvents();
14774             return;
14775         }
14776         
14777         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14778         
14779         if(this.hiddenName){
14780             
14781             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14782             
14783             this.hiddenField.dom.value =
14784                 this.hiddenValue !== undefined ? this.hiddenValue :
14785                 this.value !== undefined ? this.value : '';
14786
14787             // prevent input submission
14788             this.el.dom.removeAttribute('name');
14789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14790              
14791              
14792         }
14793         //if(Roo.isGecko){
14794         //    this.el.dom.setAttribute('autocomplete', 'off');
14795         //}
14796         
14797         var cls = 'x-combo-list';
14798         
14799         //this.list = new Roo.Layer({
14800         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14801         //});
14802         
14803         var _this = this;
14804         
14805         (function(){
14806             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14807             _this.list.setWidth(lw);
14808         }).defer(100);
14809         
14810         this.list.on('mouseover', this.onViewOver, this);
14811         this.list.on('mousemove', this.onViewMove, this);
14812         this.list.on('scroll', this.onViewScroll, this);
14813         
14814         /*
14815         this.list.swallowEvent('mousewheel');
14816         this.assetHeight = 0;
14817
14818         if(this.title){
14819             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14820             this.assetHeight += this.header.getHeight();
14821         }
14822
14823         this.innerList = this.list.createChild({cls:cls+'-inner'});
14824         this.innerList.on('mouseover', this.onViewOver, this);
14825         this.innerList.on('mousemove', this.onViewMove, this);
14826         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14827         
14828         if(this.allowBlank && !this.pageSize && !this.disableClear){
14829             this.footer = this.list.createChild({cls:cls+'-ft'});
14830             this.pageTb = new Roo.Toolbar(this.footer);
14831            
14832         }
14833         if(this.pageSize){
14834             this.footer = this.list.createChild({cls:cls+'-ft'});
14835             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14836                     {pageSize: this.pageSize});
14837             
14838         }
14839         
14840         if (this.pageTb && this.allowBlank && !this.disableClear) {
14841             var _this = this;
14842             this.pageTb.add(new Roo.Toolbar.Fill(), {
14843                 cls: 'x-btn-icon x-btn-clear',
14844                 text: '&#160;',
14845                 handler: function()
14846                 {
14847                     _this.collapse();
14848                     _this.clearValue();
14849                     _this.onSelect(false, -1);
14850                 }
14851             });
14852         }
14853         if (this.footer) {
14854             this.assetHeight += this.footer.getHeight();
14855         }
14856         */
14857             
14858         if(!this.tpl){
14859             this.tpl = Roo.bootstrap.version == 4 ?
14860                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14861                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14862         }
14863
14864         this.view = new Roo.View(this.list, this.tpl, {
14865             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14866         });
14867         //this.view.wrapEl.setDisplayed(false);
14868         this.view.on('click', this.onViewClick, this);
14869         
14870         
14871         this.store.on('beforeload', this.onBeforeLoad, this);
14872         this.store.on('load', this.onLoad, this);
14873         this.store.on('loadexception', this.onLoadException, this);
14874         /*
14875         if(this.resizable){
14876             this.resizer = new Roo.Resizable(this.list,  {
14877                pinned:true, handles:'se'
14878             });
14879             this.resizer.on('resize', function(r, w, h){
14880                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14881                 this.listWidth = w;
14882                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14883                 this.restrictHeight();
14884             }, this);
14885             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14886         }
14887         */
14888         if(!this.editable){
14889             this.editable = true;
14890             this.setEditable(false);
14891         }
14892         
14893         /*
14894         
14895         if (typeof(this.events.add.listeners) != 'undefined') {
14896             
14897             this.addicon = this.wrap.createChild(
14898                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14899        
14900             this.addicon.on('click', function(e) {
14901                 this.fireEvent('add', this);
14902             }, this);
14903         }
14904         if (typeof(this.events.edit.listeners) != 'undefined') {
14905             
14906             this.editicon = this.wrap.createChild(
14907                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14908             if (this.addicon) {
14909                 this.editicon.setStyle('margin-left', '40px');
14910             }
14911             this.editicon.on('click', function(e) {
14912                 
14913                 // we fire even  if inothing is selected..
14914                 this.fireEvent('edit', this, this.lastData );
14915                 
14916             }, this);
14917         }
14918         */
14919         
14920         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14921             "up" : function(e){
14922                 this.inKeyMode = true;
14923                 this.selectPrev();
14924             },
14925
14926             "down" : function(e){
14927                 if(!this.isExpanded()){
14928                     this.onTriggerClick();
14929                 }else{
14930                     this.inKeyMode = true;
14931                     this.selectNext();
14932                 }
14933             },
14934
14935             "enter" : function(e){
14936 //                this.onViewClick();
14937                 //return true;
14938                 this.collapse();
14939                 
14940                 if(this.fireEvent("specialkey", this, e)){
14941                     this.onViewClick(false);
14942                 }
14943                 
14944                 return true;
14945             },
14946
14947             "esc" : function(e){
14948                 this.collapse();
14949             },
14950
14951             "tab" : function(e){
14952                 this.collapse();
14953                 
14954                 if(this.fireEvent("specialkey", this, e)){
14955                     this.onViewClick(false);
14956                 }
14957                 
14958                 return true;
14959             },
14960
14961             scope : this,
14962
14963             doRelay : function(foo, bar, hname){
14964                 if(hname == 'down' || this.scope.isExpanded()){
14965                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14966                 }
14967                 return true;
14968             },
14969
14970             forceKeyDown: true
14971         });
14972         
14973         
14974         this.queryDelay = Math.max(this.queryDelay || 10,
14975                 this.mode == 'local' ? 10 : 250);
14976         
14977         
14978         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14979         
14980         if(this.typeAhead){
14981             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14982         }
14983         if(this.editable !== false){
14984             this.inputEl().on("keyup", this.onKeyUp, this);
14985         }
14986         if(this.forceSelection){
14987             this.inputEl().on('blur', this.doForce, this);
14988         }
14989         
14990         if(this.multiple){
14991             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14992             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14993         }
14994     },
14995     
14996     initTickableEvents: function()
14997     {   
14998         this.createList();
14999         
15000         if(this.hiddenName){
15001             
15002             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15003             
15004             this.hiddenField.dom.value =
15005                 this.hiddenValue !== undefined ? this.hiddenValue :
15006                 this.value !== undefined ? this.value : '';
15007
15008             // prevent input submission
15009             this.el.dom.removeAttribute('name');
15010             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15011              
15012              
15013         }
15014         
15015 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15016         
15017         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15018         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15019         if(this.triggerList){
15020             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15021         }
15022          
15023         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15024         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15025         
15026         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15027         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15028         
15029         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15030         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15031         
15032         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15033         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15034         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15035         
15036         this.okBtn.hide();
15037         this.cancelBtn.hide();
15038         
15039         var _this = this;
15040         
15041         (function(){
15042             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15043             _this.list.setWidth(lw);
15044         }).defer(100);
15045         
15046         this.list.on('mouseover', this.onViewOver, this);
15047         this.list.on('mousemove', this.onViewMove, this);
15048         
15049         this.list.on('scroll', this.onViewScroll, this);
15050         
15051         if(!this.tpl){
15052             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15053                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15054         }
15055
15056         this.view = new Roo.View(this.list, this.tpl, {
15057             singleSelect:true,
15058             tickable:true,
15059             parent:this,
15060             store: this.store,
15061             selectedClass: this.selectedClass
15062         });
15063         
15064         //this.view.wrapEl.setDisplayed(false);
15065         this.view.on('click', this.onViewClick, this);
15066         
15067         
15068         
15069         this.store.on('beforeload', this.onBeforeLoad, this);
15070         this.store.on('load', this.onLoad, this);
15071         this.store.on('loadexception', this.onLoadException, this);
15072         
15073         if(this.editable){
15074             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15075                 "up" : function(e){
15076                     this.inKeyMode = true;
15077                     this.selectPrev();
15078                 },
15079
15080                 "down" : function(e){
15081                     this.inKeyMode = true;
15082                     this.selectNext();
15083                 },
15084
15085                 "enter" : function(e){
15086                     if(this.fireEvent("specialkey", this, e)){
15087                         this.onViewClick(false);
15088                     }
15089                     
15090                     return true;
15091                 },
15092
15093                 "esc" : function(e){
15094                     this.onTickableFooterButtonClick(e, false, false);
15095                 },
15096
15097                 "tab" : function(e){
15098                     this.fireEvent("specialkey", this, e);
15099                     
15100                     this.onTickableFooterButtonClick(e, false, false);
15101                     
15102                     return true;
15103                 },
15104
15105                 scope : this,
15106
15107                 doRelay : function(e, fn, key){
15108                     if(this.scope.isExpanded()){
15109                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15110                     }
15111                     return true;
15112                 },
15113
15114                 forceKeyDown: true
15115             });
15116         }
15117         
15118         this.queryDelay = Math.max(this.queryDelay || 10,
15119                 this.mode == 'local' ? 10 : 250);
15120         
15121         
15122         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15123         
15124         if(this.typeAhead){
15125             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15126         }
15127         
15128         if(this.editable !== false){
15129             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15130         }
15131         
15132         this.indicator = this.indicatorEl();
15133         
15134         if(this.indicator){
15135             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15136             this.indicator.hide();
15137         }
15138         
15139     },
15140
15141     onDestroy : function(){
15142         if(this.view){
15143             this.view.setStore(null);
15144             this.view.el.removeAllListeners();
15145             this.view.el.remove();
15146             this.view.purgeListeners();
15147         }
15148         if(this.list){
15149             this.list.dom.innerHTML  = '';
15150         }
15151         
15152         if(this.store){
15153             this.store.un('beforeload', this.onBeforeLoad, this);
15154             this.store.un('load', this.onLoad, this);
15155             this.store.un('loadexception', this.onLoadException, this);
15156         }
15157         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15158     },
15159
15160     // private
15161     fireKey : function(e){
15162         if(e.isNavKeyPress() && !this.list.isVisible()){
15163             this.fireEvent("specialkey", this, e);
15164         }
15165     },
15166
15167     // private
15168     onResize: function(w, h){
15169 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15170 //        
15171 //        if(typeof w != 'number'){
15172 //            // we do not handle it!?!?
15173 //            return;
15174 //        }
15175 //        var tw = this.trigger.getWidth();
15176 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15177 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15178 //        var x = w - tw;
15179 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15180 //            
15181 //        //this.trigger.setStyle('left', x+'px');
15182 //        
15183 //        if(this.list && this.listWidth === undefined){
15184 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15185 //            this.list.setWidth(lw);
15186 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15187 //        }
15188         
15189     
15190         
15191     },
15192
15193     /**
15194      * Allow or prevent the user from directly editing the field text.  If false is passed,
15195      * the user will only be able to select from the items defined in the dropdown list.  This method
15196      * is the runtime equivalent of setting the 'editable' config option at config time.
15197      * @param {Boolean} value True to allow the user to directly edit the field text
15198      */
15199     setEditable : function(value){
15200         if(value == this.editable){
15201             return;
15202         }
15203         this.editable = value;
15204         if(!value){
15205             this.inputEl().dom.setAttribute('readOnly', true);
15206             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15207             this.inputEl().addClass('x-combo-noedit');
15208         }else{
15209             this.inputEl().dom.setAttribute('readOnly', false);
15210             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15211             this.inputEl().removeClass('x-combo-noedit');
15212         }
15213     },
15214
15215     // private
15216     
15217     onBeforeLoad : function(combo,opts){
15218         if(!this.hasFocus){
15219             return;
15220         }
15221          if (!opts.add) {
15222             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15223          }
15224         this.restrictHeight();
15225         this.selectedIndex = -1;
15226     },
15227
15228     // private
15229     onLoad : function(){
15230         
15231         this.hasQuery = false;
15232         
15233         if(!this.hasFocus){
15234             return;
15235         }
15236         
15237         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15238             this.loading.hide();
15239         }
15240         
15241         if(this.store.getCount() > 0){
15242             
15243             this.expand();
15244             this.restrictHeight();
15245             if(this.lastQuery == this.allQuery){
15246                 if(this.editable && !this.tickable){
15247                     this.inputEl().dom.select();
15248                 }
15249                 
15250                 if(
15251                     !this.selectByValue(this.value, true) &&
15252                     this.autoFocus && 
15253                     (
15254                         !this.store.lastOptions ||
15255                         typeof(this.store.lastOptions.add) == 'undefined' || 
15256                         this.store.lastOptions.add != true
15257                     )
15258                 ){
15259                     this.select(0, true);
15260                 }
15261             }else{
15262                 if(this.autoFocus){
15263                     this.selectNext();
15264                 }
15265                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15266                     this.taTask.delay(this.typeAheadDelay);
15267                 }
15268             }
15269         }else{
15270             this.onEmptyResults();
15271         }
15272         
15273         //this.el.focus();
15274     },
15275     // private
15276     onLoadException : function()
15277     {
15278         this.hasQuery = false;
15279         
15280         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15281             this.loading.hide();
15282         }
15283         
15284         if(this.tickable && this.editable){
15285             return;
15286         }
15287         
15288         this.collapse();
15289         // only causes errors at present
15290         //Roo.log(this.store.reader.jsonData);
15291         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15292             // fixme
15293             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15294         //}
15295         
15296         
15297     },
15298     // private
15299     onTypeAhead : function(){
15300         if(this.store.getCount() > 0){
15301             var r = this.store.getAt(0);
15302             var newValue = r.data[this.displayField];
15303             var len = newValue.length;
15304             var selStart = this.getRawValue().length;
15305             
15306             if(selStart != len){
15307                 this.setRawValue(newValue);
15308                 this.selectText(selStart, newValue.length);
15309             }
15310         }
15311     },
15312
15313     // private
15314     onSelect : function(record, index){
15315         
15316         if(this.fireEvent('beforeselect', this, record, index) !== false){
15317         
15318             this.setFromData(index > -1 ? record.data : false);
15319             
15320             this.collapse();
15321             this.fireEvent('select', this, record, index);
15322         }
15323     },
15324
15325     /**
15326      * Returns the currently selected field value or empty string if no value is set.
15327      * @return {String} value The selected value
15328      */
15329     getValue : function()
15330     {
15331         if(Roo.isIOS && this.useNativeIOS){
15332             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15333         }
15334         
15335         if(this.multiple){
15336             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15337         }
15338         
15339         if(this.valueField){
15340             return typeof this.value != 'undefined' ? this.value : '';
15341         }else{
15342             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15343         }
15344     },
15345     
15346     getRawValue : function()
15347     {
15348         if(Roo.isIOS && this.useNativeIOS){
15349             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15350         }
15351         
15352         var v = this.inputEl().getValue();
15353         
15354         return v;
15355     },
15356
15357     /**
15358      * Clears any text/value currently set in the field
15359      */
15360     clearValue : function(){
15361         
15362         if(this.hiddenField){
15363             this.hiddenField.dom.value = '';
15364         }
15365         this.value = '';
15366         this.setRawValue('');
15367         this.lastSelectionText = '';
15368         this.lastData = false;
15369         
15370         var close = this.closeTriggerEl();
15371         
15372         if(close){
15373             close.hide();
15374         }
15375         
15376         this.validate();
15377         
15378     },
15379
15380     /**
15381      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15382      * will be displayed in the field.  If the value does not match the data value of an existing item,
15383      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15384      * Otherwise the field will be blank (although the value will still be set).
15385      * @param {String} value The value to match
15386      */
15387     setValue : function(v)
15388     {
15389         if(Roo.isIOS && this.useNativeIOS){
15390             this.setIOSValue(v);
15391             return;
15392         }
15393         
15394         if(this.multiple){
15395             this.syncValue();
15396             return;
15397         }
15398         
15399         var text = v;
15400         if(this.valueField){
15401             var r = this.findRecord(this.valueField, v);
15402             if(r){
15403                 text = r.data[this.displayField];
15404             }else if(this.valueNotFoundText !== undefined){
15405                 text = this.valueNotFoundText;
15406             }
15407         }
15408         this.lastSelectionText = text;
15409         if(this.hiddenField){
15410             this.hiddenField.dom.value = v;
15411         }
15412         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15413         this.value = v;
15414         
15415         var close = this.closeTriggerEl();
15416         
15417         if(close){
15418             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15419         }
15420         
15421         this.validate();
15422     },
15423     /**
15424      * @property {Object} the last set data for the element
15425      */
15426     
15427     lastData : false,
15428     /**
15429      * Sets the value of the field based on a object which is related to the record format for the store.
15430      * @param {Object} value the value to set as. or false on reset?
15431      */
15432     setFromData : function(o){
15433         
15434         if(this.multiple){
15435             this.addItem(o);
15436             return;
15437         }
15438             
15439         var dv = ''; // display value
15440         var vv = ''; // value value..
15441         this.lastData = o;
15442         if (this.displayField) {
15443             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15444         } else {
15445             // this is an error condition!!!
15446             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15447         }
15448         
15449         if(this.valueField){
15450             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15451         }
15452         
15453         var close = this.closeTriggerEl();
15454         
15455         if(close){
15456             if(dv.length || vv * 1 > 0){
15457                 close.show() ;
15458                 this.blockFocus=true;
15459             } else {
15460                 close.hide();
15461             }             
15462         }
15463         
15464         if(this.hiddenField){
15465             this.hiddenField.dom.value = vv;
15466             
15467             this.lastSelectionText = dv;
15468             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15469             this.value = vv;
15470             return;
15471         }
15472         // no hidden field.. - we store the value in 'value', but still display
15473         // display field!!!!
15474         this.lastSelectionText = dv;
15475         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15476         this.value = vv;
15477         
15478         
15479         
15480     },
15481     // private
15482     reset : function(){
15483         // overridden so that last data is reset..
15484         
15485         if(this.multiple){
15486             this.clearItem();
15487             return;
15488         }
15489         
15490         this.setValue(this.originalValue);
15491         //this.clearInvalid();
15492         this.lastData = false;
15493         if (this.view) {
15494             this.view.clearSelections();
15495         }
15496         
15497         this.validate();
15498     },
15499     // private
15500     findRecord : function(prop, value){
15501         var record;
15502         if(this.store.getCount() > 0){
15503             this.store.each(function(r){
15504                 if(r.data[prop] == value){
15505                     record = r;
15506                     return false;
15507                 }
15508                 return true;
15509             });
15510         }
15511         return record;
15512     },
15513     
15514     getName: function()
15515     {
15516         // returns hidden if it's set..
15517         if (!this.rendered) {return ''};
15518         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15519         
15520     },
15521     // private
15522     onViewMove : function(e, t){
15523         this.inKeyMode = false;
15524     },
15525
15526     // private
15527     onViewOver : function(e, t){
15528         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15529             return;
15530         }
15531         var item = this.view.findItemFromChild(t);
15532         
15533         if(item){
15534             var index = this.view.indexOf(item);
15535             this.select(index, false);
15536         }
15537     },
15538
15539     // private
15540     onViewClick : function(view, doFocus, el, e)
15541     {
15542         var index = this.view.getSelectedIndexes()[0];
15543         
15544         var r = this.store.getAt(index);
15545         
15546         if(this.tickable){
15547             
15548             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15549                 return;
15550             }
15551             
15552             var rm = false;
15553             var _this = this;
15554             
15555             Roo.each(this.tickItems, function(v,k){
15556                 
15557                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15558                     Roo.log(v);
15559                     _this.tickItems.splice(k, 1);
15560                     
15561                     if(typeof(e) == 'undefined' && view == false){
15562                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15563                     }
15564                     
15565                     rm = true;
15566                     return;
15567                 }
15568             });
15569             
15570             if(rm){
15571                 return;
15572             }
15573             
15574             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15575                 this.tickItems.push(r.data);
15576             }
15577             
15578             if(typeof(e) == 'undefined' && view == false){
15579                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15580             }
15581                     
15582             return;
15583         }
15584         
15585         if(r){
15586             this.onSelect(r, index);
15587         }
15588         if(doFocus !== false && !this.blockFocus){
15589             this.inputEl().focus();
15590         }
15591     },
15592
15593     // private
15594     restrictHeight : function(){
15595         //this.innerList.dom.style.height = '';
15596         //var inner = this.innerList.dom;
15597         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15598         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15599         //this.list.beginUpdate();
15600         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15601         this.list.alignTo(this.inputEl(), this.listAlign);
15602         this.list.alignTo(this.inputEl(), this.listAlign);
15603         //this.list.endUpdate();
15604     },
15605
15606     // private
15607     onEmptyResults : function(){
15608         
15609         if(this.tickable && this.editable){
15610             this.hasFocus = false;
15611             this.restrictHeight();
15612             return;
15613         }
15614         
15615         this.collapse();
15616     },
15617
15618     /**
15619      * Returns true if the dropdown list is expanded, else false.
15620      */
15621     isExpanded : function(){
15622         return this.list.isVisible();
15623     },
15624
15625     /**
15626      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15627      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15628      * @param {String} value The data value of the item to select
15629      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15630      * selected item if it is not currently in view (defaults to true)
15631      * @return {Boolean} True if the value matched an item in the list, else false
15632      */
15633     selectByValue : function(v, scrollIntoView){
15634         if(v !== undefined && v !== null){
15635             var r = this.findRecord(this.valueField || this.displayField, v);
15636             if(r){
15637                 this.select(this.store.indexOf(r), scrollIntoView);
15638                 return true;
15639             }
15640         }
15641         return false;
15642     },
15643
15644     /**
15645      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15646      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15647      * @param {Number} index The zero-based index of the list item to select
15648      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15649      * selected item if it is not currently in view (defaults to true)
15650      */
15651     select : function(index, scrollIntoView){
15652         this.selectedIndex = index;
15653         this.view.select(index);
15654         if(scrollIntoView !== false){
15655             var el = this.view.getNode(index);
15656             /*
15657              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15658              */
15659             if(el){
15660                 this.list.scrollChildIntoView(el, false);
15661             }
15662         }
15663     },
15664
15665     // private
15666     selectNext : function(){
15667         var ct = this.store.getCount();
15668         if(ct > 0){
15669             if(this.selectedIndex == -1){
15670                 this.select(0);
15671             }else if(this.selectedIndex < ct-1){
15672                 this.select(this.selectedIndex+1);
15673             }
15674         }
15675     },
15676
15677     // private
15678     selectPrev : function(){
15679         var ct = this.store.getCount();
15680         if(ct > 0){
15681             if(this.selectedIndex == -1){
15682                 this.select(0);
15683             }else if(this.selectedIndex != 0){
15684                 this.select(this.selectedIndex-1);
15685             }
15686         }
15687     },
15688
15689     // private
15690     onKeyUp : function(e){
15691         if(this.editable !== false && !e.isSpecialKey()){
15692             this.lastKey = e.getKey();
15693             this.dqTask.delay(this.queryDelay);
15694         }
15695     },
15696
15697     // private
15698     validateBlur : function(){
15699         return !this.list || !this.list.isVisible();   
15700     },
15701
15702     // private
15703     initQuery : function(){
15704         
15705         var v = this.getRawValue();
15706         
15707         if(this.tickable && this.editable){
15708             v = this.tickableInputEl().getValue();
15709         }
15710         
15711         this.doQuery(v);
15712     },
15713
15714     // private
15715     doForce : function(){
15716         if(this.inputEl().dom.value.length > 0){
15717             this.inputEl().dom.value =
15718                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15719              
15720         }
15721     },
15722
15723     /**
15724      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15725      * query allowing the query action to be canceled if needed.
15726      * @param {String} query The SQL query to execute
15727      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15728      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15729      * saved in the current store (defaults to false)
15730      */
15731     doQuery : function(q, forceAll){
15732         
15733         if(q === undefined || q === null){
15734             q = '';
15735         }
15736         var qe = {
15737             query: q,
15738             forceAll: forceAll,
15739             combo: this,
15740             cancel:false
15741         };
15742         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15743             return false;
15744         }
15745         q = qe.query;
15746         
15747         forceAll = qe.forceAll;
15748         if(forceAll === true || (q.length >= this.minChars)){
15749             
15750             this.hasQuery = true;
15751             
15752             if(this.lastQuery != q || this.alwaysQuery){
15753                 this.lastQuery = q;
15754                 if(this.mode == 'local'){
15755                     this.selectedIndex = -1;
15756                     if(forceAll){
15757                         this.store.clearFilter();
15758                     }else{
15759                         
15760                         if(this.specialFilter){
15761                             this.fireEvent('specialfilter', this);
15762                             this.onLoad();
15763                             return;
15764                         }
15765                         
15766                         this.store.filter(this.displayField, q);
15767                     }
15768                     
15769                     this.store.fireEvent("datachanged", this.store);
15770                     
15771                     this.onLoad();
15772                     
15773                     
15774                 }else{
15775                     
15776                     this.store.baseParams[this.queryParam] = q;
15777                     
15778                     var options = {params : this.getParams(q)};
15779                     
15780                     if(this.loadNext){
15781                         options.add = true;
15782                         options.params.start = this.page * this.pageSize;
15783                     }
15784                     
15785                     this.store.load(options);
15786                     
15787                     /*
15788                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15789                      *  we should expand the list on onLoad
15790                      *  so command out it
15791                      */
15792 //                    this.expand();
15793                 }
15794             }else{
15795                 this.selectedIndex = -1;
15796                 this.onLoad();   
15797             }
15798         }
15799         
15800         this.loadNext = false;
15801     },
15802     
15803     // private
15804     getParams : function(q){
15805         var p = {};
15806         //p[this.queryParam] = q;
15807         
15808         if(this.pageSize){
15809             p.start = 0;
15810             p.limit = this.pageSize;
15811         }
15812         return p;
15813     },
15814
15815     /**
15816      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15817      */
15818     collapse : function(){
15819         if(!this.isExpanded()){
15820             return;
15821         }
15822         
15823         this.list.hide();
15824         
15825         this.hasFocus = false;
15826         
15827         if(this.tickable){
15828             this.okBtn.hide();
15829             this.cancelBtn.hide();
15830             this.trigger.show();
15831             
15832             if(this.editable){
15833                 this.tickableInputEl().dom.value = '';
15834                 this.tickableInputEl().blur();
15835             }
15836             
15837         }
15838         
15839         Roo.get(document).un('mousedown', this.collapseIf, this);
15840         Roo.get(document).un('mousewheel', this.collapseIf, this);
15841         if (!this.editable) {
15842             Roo.get(document).un('keydown', this.listKeyPress, this);
15843         }
15844         this.fireEvent('collapse', this);
15845         
15846         this.validate();
15847     },
15848
15849     // private
15850     collapseIf : function(e){
15851         var in_combo  = e.within(this.el);
15852         var in_list =  e.within(this.list);
15853         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15854         
15855         if (in_combo || in_list || is_list) {
15856             //e.stopPropagation();
15857             return;
15858         }
15859         
15860         if(this.tickable){
15861             this.onTickableFooterButtonClick(e, false, false);
15862         }
15863
15864         this.collapse();
15865         
15866     },
15867
15868     /**
15869      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15870      */
15871     expand : function(){
15872        
15873         if(this.isExpanded() || !this.hasFocus){
15874             return;
15875         }
15876         
15877         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15878         this.list.setWidth(lw);
15879         
15880         Roo.log('expand');
15881         
15882         this.list.show();
15883         
15884         this.restrictHeight();
15885         
15886         if(this.tickable){
15887             
15888             this.tickItems = Roo.apply([], this.item);
15889             
15890             this.okBtn.show();
15891             this.cancelBtn.show();
15892             this.trigger.hide();
15893             
15894             if(this.editable){
15895                 this.tickableInputEl().focus();
15896             }
15897             
15898         }
15899         
15900         Roo.get(document).on('mousedown', this.collapseIf, this);
15901         Roo.get(document).on('mousewheel', this.collapseIf, this);
15902         if (!this.editable) {
15903             Roo.get(document).on('keydown', this.listKeyPress, this);
15904         }
15905         
15906         this.fireEvent('expand', this);
15907     },
15908
15909     // private
15910     // Implements the default empty TriggerField.onTriggerClick function
15911     onTriggerClick : function(e)
15912     {
15913         Roo.log('trigger click');
15914         
15915         if(this.disabled || !this.triggerList){
15916             return;
15917         }
15918         
15919         this.page = 0;
15920         this.loadNext = false;
15921         
15922         if(this.isExpanded()){
15923             this.collapse();
15924             if (!this.blockFocus) {
15925                 this.inputEl().focus();
15926             }
15927             
15928         }else {
15929             this.hasFocus = true;
15930             if(this.triggerAction == 'all') {
15931                 this.doQuery(this.allQuery, true);
15932             } else {
15933                 this.doQuery(this.getRawValue());
15934             }
15935             if (!this.blockFocus) {
15936                 this.inputEl().focus();
15937             }
15938         }
15939     },
15940     
15941     onTickableTriggerClick : function(e)
15942     {
15943         if(this.disabled){
15944             return;
15945         }
15946         
15947         this.page = 0;
15948         this.loadNext = false;
15949         this.hasFocus = true;
15950         
15951         if(this.triggerAction == 'all') {
15952             this.doQuery(this.allQuery, true);
15953         } else {
15954             this.doQuery(this.getRawValue());
15955         }
15956     },
15957     
15958     onSearchFieldClick : function(e)
15959     {
15960         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15961             this.onTickableFooterButtonClick(e, false, false);
15962             return;
15963         }
15964         
15965         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15966             return;
15967         }
15968         
15969         this.page = 0;
15970         this.loadNext = false;
15971         this.hasFocus = true;
15972         
15973         if(this.triggerAction == 'all') {
15974             this.doQuery(this.allQuery, true);
15975         } else {
15976             this.doQuery(this.getRawValue());
15977         }
15978     },
15979     
15980     listKeyPress : function(e)
15981     {
15982         //Roo.log('listkeypress');
15983         // scroll to first matching element based on key pres..
15984         if (e.isSpecialKey()) {
15985             return false;
15986         }
15987         var k = String.fromCharCode(e.getKey()).toUpperCase();
15988         //Roo.log(k);
15989         var match  = false;
15990         var csel = this.view.getSelectedNodes();
15991         var cselitem = false;
15992         if (csel.length) {
15993             var ix = this.view.indexOf(csel[0]);
15994             cselitem  = this.store.getAt(ix);
15995             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15996                 cselitem = false;
15997             }
15998             
15999         }
16000         
16001         this.store.each(function(v) { 
16002             if (cselitem) {
16003                 // start at existing selection.
16004                 if (cselitem.id == v.id) {
16005                     cselitem = false;
16006                 }
16007                 return true;
16008             }
16009                 
16010             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16011                 match = this.store.indexOf(v);
16012                 return false;
16013             }
16014             return true;
16015         }, this);
16016         
16017         if (match === false) {
16018             return true; // no more action?
16019         }
16020         // scroll to?
16021         this.view.select(match);
16022         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16023         sn.scrollIntoView(sn.dom.parentNode, false);
16024     },
16025     
16026     onViewScroll : function(e, t){
16027         
16028         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){
16029             return;
16030         }
16031         
16032         this.hasQuery = true;
16033         
16034         this.loading = this.list.select('.loading', true).first();
16035         
16036         if(this.loading === null){
16037             this.list.createChild({
16038                 tag: 'div',
16039                 cls: 'loading roo-select2-more-results roo-select2-active',
16040                 html: 'Loading more results...'
16041             });
16042             
16043             this.loading = this.list.select('.loading', true).first();
16044             
16045             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16046             
16047             this.loading.hide();
16048         }
16049         
16050         this.loading.show();
16051         
16052         var _combo = this;
16053         
16054         this.page++;
16055         this.loadNext = true;
16056         
16057         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16058         
16059         return;
16060     },
16061     
16062     addItem : function(o)
16063     {   
16064         var dv = ''; // display value
16065         
16066         if (this.displayField) {
16067             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16068         } else {
16069             // this is an error condition!!!
16070             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16071         }
16072         
16073         if(!dv.length){
16074             return;
16075         }
16076         
16077         var choice = this.choices.createChild({
16078             tag: 'li',
16079             cls: 'roo-select2-search-choice',
16080             cn: [
16081                 {
16082                     tag: 'div',
16083                     html: dv
16084                 },
16085                 {
16086                     tag: 'a',
16087                     href: '#',
16088                     cls: 'roo-select2-search-choice-close fa fa-times',
16089                     tabindex: '-1'
16090                 }
16091             ]
16092             
16093         }, this.searchField);
16094         
16095         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16096         
16097         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16098         
16099         this.item.push(o);
16100         
16101         this.lastData = o;
16102         
16103         this.syncValue();
16104         
16105         this.inputEl().dom.value = '';
16106         
16107         this.validate();
16108     },
16109     
16110     onRemoveItem : function(e, _self, o)
16111     {
16112         e.preventDefault();
16113         
16114         this.lastItem = Roo.apply([], this.item);
16115         
16116         var index = this.item.indexOf(o.data) * 1;
16117         
16118         if( index < 0){
16119             Roo.log('not this item?!');
16120             return;
16121         }
16122         
16123         this.item.splice(index, 1);
16124         o.item.remove();
16125         
16126         this.syncValue();
16127         
16128         this.fireEvent('remove', this, e);
16129         
16130         this.validate();
16131         
16132     },
16133     
16134     syncValue : function()
16135     {
16136         if(!this.item.length){
16137             this.clearValue();
16138             return;
16139         }
16140             
16141         var value = [];
16142         var _this = this;
16143         Roo.each(this.item, function(i){
16144             if(_this.valueField){
16145                 value.push(i[_this.valueField]);
16146                 return;
16147             }
16148
16149             value.push(i);
16150         });
16151
16152         this.value = value.join(',');
16153
16154         if(this.hiddenField){
16155             this.hiddenField.dom.value = this.value;
16156         }
16157         
16158         this.store.fireEvent("datachanged", this.store);
16159         
16160         this.validate();
16161     },
16162     
16163     clearItem : function()
16164     {
16165         if(!this.multiple){
16166             return;
16167         }
16168         
16169         this.item = [];
16170         
16171         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16172            c.remove();
16173         });
16174         
16175         this.syncValue();
16176         
16177         this.validate();
16178         
16179         if(this.tickable && !Roo.isTouch){
16180             this.view.refresh();
16181         }
16182     },
16183     
16184     inputEl: function ()
16185     {
16186         if(Roo.isIOS && this.useNativeIOS){
16187             return this.el.select('select.roo-ios-select', true).first();
16188         }
16189         
16190         if(Roo.isTouch && this.mobileTouchView){
16191             return this.el.select('input.form-control',true).first();
16192         }
16193         
16194         if(this.tickable){
16195             return this.searchField;
16196         }
16197         
16198         return this.el.select('input.form-control',true).first();
16199     },
16200     
16201     onTickableFooterButtonClick : function(e, btn, el)
16202     {
16203         e.preventDefault();
16204         
16205         this.lastItem = Roo.apply([], this.item);
16206         
16207         if(btn && btn.name == 'cancel'){
16208             this.tickItems = Roo.apply([], this.item);
16209             this.collapse();
16210             return;
16211         }
16212         
16213         this.clearItem();
16214         
16215         var _this = this;
16216         
16217         Roo.each(this.tickItems, function(o){
16218             _this.addItem(o);
16219         });
16220         
16221         this.collapse();
16222         
16223     },
16224     
16225     validate : function()
16226     {
16227         if(this.getVisibilityEl().hasClass('hidden')){
16228             return true;
16229         }
16230         
16231         var v = this.getRawValue();
16232         
16233         if(this.multiple){
16234             v = this.getValue();
16235         }
16236         
16237         if(this.disabled || this.allowBlank || v.length){
16238             this.markValid();
16239             return true;
16240         }
16241         
16242         this.markInvalid();
16243         return false;
16244     },
16245     
16246     tickableInputEl : function()
16247     {
16248         if(!this.tickable || !this.editable){
16249             return this.inputEl();
16250         }
16251         
16252         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16253     },
16254     
16255     
16256     getAutoCreateTouchView : function()
16257     {
16258         var id = Roo.id();
16259         
16260         var cfg = {
16261             cls: 'form-group' //input-group
16262         };
16263         
16264         var input =  {
16265             tag: 'input',
16266             id : id,
16267             type : this.inputType,
16268             cls : 'form-control x-combo-noedit',
16269             autocomplete: 'new-password',
16270             placeholder : this.placeholder || '',
16271             readonly : true
16272         };
16273         
16274         if (this.name) {
16275             input.name = this.name;
16276         }
16277         
16278         if (this.size) {
16279             input.cls += ' input-' + this.size;
16280         }
16281         
16282         if (this.disabled) {
16283             input.disabled = true;
16284         }
16285         
16286         var inputblock = {
16287             cls : '',
16288             cn : [
16289                 input
16290             ]
16291         };
16292         
16293         if(this.before){
16294             inputblock.cls += ' input-group';
16295             
16296             inputblock.cn.unshift({
16297                 tag :'span',
16298                 cls : 'input-group-addon input-group-prepend input-group-text',
16299                 html : this.before
16300             });
16301         }
16302         
16303         if(this.removable && !this.multiple){
16304             inputblock.cls += ' roo-removable';
16305             
16306             inputblock.cn.push({
16307                 tag: 'button',
16308                 html : 'x',
16309                 cls : 'roo-combo-removable-btn close'
16310             });
16311         }
16312
16313         if(this.hasFeedback && !this.allowBlank){
16314             
16315             inputblock.cls += ' has-feedback';
16316             
16317             inputblock.cn.push({
16318                 tag: 'span',
16319                 cls: 'glyphicon form-control-feedback'
16320             });
16321             
16322         }
16323         
16324         if (this.after) {
16325             
16326             inputblock.cls += (this.before) ? '' : ' input-group';
16327             
16328             inputblock.cn.push({
16329                 tag :'span',
16330                 cls : 'input-group-addon input-group-append input-group-text',
16331                 html : this.after
16332             });
16333         }
16334
16335         
16336         var ibwrap = inputblock;
16337         
16338         if(this.multiple){
16339             ibwrap = {
16340                 tag: 'ul',
16341                 cls: 'roo-select2-choices',
16342                 cn:[
16343                     {
16344                         tag: 'li',
16345                         cls: 'roo-select2-search-field',
16346                         cn: [
16347
16348                             inputblock
16349                         ]
16350                     }
16351                 ]
16352             };
16353         
16354             
16355         }
16356         
16357         var combobox = {
16358             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16359             cn: [
16360                 {
16361                     tag: 'input',
16362                     type : 'hidden',
16363                     cls: 'form-hidden-field'
16364                 },
16365                 ibwrap
16366             ]
16367         };
16368         
16369         if(!this.multiple && this.showToggleBtn){
16370             
16371             var caret = {
16372                 cls: 'caret'
16373             };
16374             
16375             if (this.caret != false) {
16376                 caret = {
16377                      tag: 'i',
16378                      cls: 'fa fa-' + this.caret
16379                 };
16380                 
16381             }
16382             
16383             combobox.cn.push({
16384                 tag :'span',
16385                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16386                 cn : [
16387                     Roo.bootstrap.version == 3 ? caret : '',
16388                     {
16389                         tag: 'span',
16390                         cls: 'combobox-clear',
16391                         cn  : [
16392                             {
16393                                 tag : 'i',
16394                                 cls: 'icon-remove'
16395                             }
16396                         ]
16397                     }
16398                 ]
16399
16400             })
16401         }
16402         
16403         if(this.multiple){
16404             combobox.cls += ' roo-select2-container-multi';
16405         }
16406         
16407         var align = this.labelAlign || this.parentLabelAlign();
16408         
16409         if (align ==='left' && this.fieldLabel.length) {
16410
16411             cfg.cn = [
16412                 {
16413                    tag : 'i',
16414                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16415                    tooltip : 'This field is required'
16416                 },
16417                 {
16418                     tag: 'label',
16419                     cls : 'control-label col-form-label',
16420                     html : this.fieldLabel
16421
16422                 },
16423                 {
16424                     cls : '', 
16425                     cn: [
16426                         combobox
16427                     ]
16428                 }
16429             ];
16430             
16431             var labelCfg = cfg.cn[1];
16432             var contentCfg = cfg.cn[2];
16433             
16434
16435             if(this.indicatorpos == 'right'){
16436                 cfg.cn = [
16437                     {
16438                         tag: 'label',
16439                         'for' :  id,
16440                         cls : 'control-label col-form-label',
16441                         cn : [
16442                             {
16443                                 tag : 'span',
16444                                 html : this.fieldLabel
16445                             },
16446                             {
16447                                 tag : 'i',
16448                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16449                                 tooltip : 'This field is required'
16450                             }
16451                         ]
16452                     },
16453                     {
16454                         cls : "",
16455                         cn: [
16456                             combobox
16457                         ]
16458                     }
16459
16460                 ];
16461                 
16462                 labelCfg = cfg.cn[0];
16463                 contentCfg = cfg.cn[1];
16464             }
16465             
16466            
16467             
16468             if(this.labelWidth > 12){
16469                 labelCfg.style = "width: " + this.labelWidth + 'px';
16470             }
16471             
16472             if(this.labelWidth < 13 && this.labelmd == 0){
16473                 this.labelmd = this.labelWidth;
16474             }
16475             
16476             if(this.labellg > 0){
16477                 labelCfg.cls += ' col-lg-' + this.labellg;
16478                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16479             }
16480             
16481             if(this.labelmd > 0){
16482                 labelCfg.cls += ' col-md-' + this.labelmd;
16483                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16484             }
16485             
16486             if(this.labelsm > 0){
16487                 labelCfg.cls += ' col-sm-' + this.labelsm;
16488                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16489             }
16490             
16491             if(this.labelxs > 0){
16492                 labelCfg.cls += ' col-xs-' + this.labelxs;
16493                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16494             }
16495                 
16496                 
16497         } else if ( this.fieldLabel.length) {
16498             cfg.cn = [
16499                 {
16500                    tag : 'i',
16501                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16502                    tooltip : 'This field is required'
16503                 },
16504                 {
16505                     tag: 'label',
16506                     cls : 'control-label',
16507                     html : this.fieldLabel
16508
16509                 },
16510                 {
16511                     cls : '', 
16512                     cn: [
16513                         combobox
16514                     ]
16515                 }
16516             ];
16517             
16518             if(this.indicatorpos == 'right'){
16519                 cfg.cn = [
16520                     {
16521                         tag: 'label',
16522                         cls : 'control-label',
16523                         html : this.fieldLabel,
16524                         cn : [
16525                             {
16526                                tag : 'i',
16527                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16528                                tooltip : 'This field is required'
16529                             }
16530                         ]
16531                     },
16532                     {
16533                         cls : '', 
16534                         cn: [
16535                             combobox
16536                         ]
16537                     }
16538                 ];
16539             }
16540         } else {
16541             cfg.cn = combobox;    
16542         }
16543         
16544         
16545         var settings = this;
16546         
16547         ['xs','sm','md','lg'].map(function(size){
16548             if (settings[size]) {
16549                 cfg.cls += ' col-' + size + '-' + settings[size];
16550             }
16551         });
16552         
16553         return cfg;
16554     },
16555     
16556     initTouchView : function()
16557     {
16558         this.renderTouchView();
16559         
16560         this.touchViewEl.on('scroll', function(){
16561             this.el.dom.scrollTop = 0;
16562         }, this);
16563         
16564         this.originalValue = this.getValue();
16565         
16566         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16567         
16568         this.inputEl().on("click", this.showTouchView, this);
16569         if (this.triggerEl) {
16570             this.triggerEl.on("click", this.showTouchView, this);
16571         }
16572         
16573         
16574         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16575         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16576         
16577         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16578         
16579         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16580         this.store.on('load', this.onTouchViewLoad, this);
16581         this.store.on('loadexception', this.onTouchViewLoadException, this);
16582         
16583         if(this.hiddenName){
16584             
16585             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16586             
16587             this.hiddenField.dom.value =
16588                 this.hiddenValue !== undefined ? this.hiddenValue :
16589                 this.value !== undefined ? this.value : '';
16590         
16591             this.el.dom.removeAttribute('name');
16592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16593         }
16594         
16595         if(this.multiple){
16596             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16597             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16598         }
16599         
16600         if(this.removable && !this.multiple){
16601             var close = this.closeTriggerEl();
16602             if(close){
16603                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16604                 close.on('click', this.removeBtnClick, this, close);
16605             }
16606         }
16607         /*
16608          * fix the bug in Safari iOS8
16609          */
16610         this.inputEl().on("focus", function(e){
16611             document.activeElement.blur();
16612         }, this);
16613         
16614         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16615         
16616         return;
16617         
16618         
16619     },
16620     
16621     renderTouchView : function()
16622     {
16623         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16624         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16625         
16626         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16627         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16628         
16629         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16630         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16631         this.touchViewBodyEl.setStyle('overflow', 'auto');
16632         
16633         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16634         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16635         
16636         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16637         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16638         
16639     },
16640     
16641     showTouchView : function()
16642     {
16643         if(this.disabled){
16644             return;
16645         }
16646         
16647         this.touchViewHeaderEl.hide();
16648
16649         if(this.modalTitle.length){
16650             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16651             this.touchViewHeaderEl.show();
16652         }
16653
16654         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16655         this.touchViewEl.show();
16656
16657         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16658         
16659         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16660         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16661
16662         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16663
16664         if(this.modalTitle.length){
16665             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16666         }
16667         
16668         this.touchViewBodyEl.setHeight(bodyHeight);
16669
16670         if(this.animate){
16671             var _this = this;
16672             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16673         }else{
16674             this.touchViewEl.addClass('in');
16675         }
16676         
16677         if(this._touchViewMask){
16678             Roo.get(document.body).addClass("x-body-masked");
16679             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16680             this._touchViewMask.setStyle('z-index', 10000);
16681             this._touchViewMask.addClass('show');
16682         }
16683         
16684         this.doTouchViewQuery();
16685         
16686     },
16687     
16688     hideTouchView : function()
16689     {
16690         this.touchViewEl.removeClass('in');
16691
16692         if(this.animate){
16693             var _this = this;
16694             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16695         }else{
16696             this.touchViewEl.setStyle('display', 'none');
16697         }
16698         
16699         if(this._touchViewMask){
16700             this._touchViewMask.removeClass('show');
16701             Roo.get(document.body).removeClass("x-body-masked");
16702         }
16703     },
16704     
16705     setTouchViewValue : function()
16706     {
16707         if(this.multiple){
16708             this.clearItem();
16709         
16710             var _this = this;
16711
16712             Roo.each(this.tickItems, function(o){
16713                 this.addItem(o);
16714             }, this);
16715         }
16716         
16717         this.hideTouchView();
16718     },
16719     
16720     doTouchViewQuery : function()
16721     {
16722         var qe = {
16723             query: '',
16724             forceAll: true,
16725             combo: this,
16726             cancel:false
16727         };
16728         
16729         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16730             return false;
16731         }
16732         
16733         if(!this.alwaysQuery || this.mode == 'local'){
16734             this.onTouchViewLoad();
16735             return;
16736         }
16737         
16738         this.store.load();
16739     },
16740     
16741     onTouchViewBeforeLoad : function(combo,opts)
16742     {
16743         return;
16744     },
16745
16746     // private
16747     onTouchViewLoad : function()
16748     {
16749         if(this.store.getCount() < 1){
16750             this.onTouchViewEmptyResults();
16751             return;
16752         }
16753         
16754         this.clearTouchView();
16755         
16756         var rawValue = this.getRawValue();
16757         
16758         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16759         
16760         this.tickItems = [];
16761         
16762         this.store.data.each(function(d, rowIndex){
16763             var row = this.touchViewListGroup.createChild(template);
16764             
16765             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16766                 row.addClass(d.data.cls);
16767             }
16768             
16769             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16770                 var cfg = {
16771                     data : d.data,
16772                     html : d.data[this.displayField]
16773                 };
16774                 
16775                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16776                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16777                 }
16778             }
16779             row.removeClass('selected');
16780             if(!this.multiple && this.valueField &&
16781                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16782             {
16783                 // radio buttons..
16784                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16785                 row.addClass('selected');
16786             }
16787             
16788             if(this.multiple && this.valueField &&
16789                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16790             {
16791                 
16792                 // checkboxes...
16793                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16794                 this.tickItems.push(d.data);
16795             }
16796             
16797             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16798             
16799         }, this);
16800         
16801         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16802         
16803         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16804
16805         if(this.modalTitle.length){
16806             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16807         }
16808
16809         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16810         
16811         if(this.mobile_restrict_height && listHeight < bodyHeight){
16812             this.touchViewBodyEl.setHeight(listHeight);
16813         }
16814         
16815         var _this = this;
16816         
16817         if(firstChecked && listHeight > bodyHeight){
16818             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16819         }
16820         
16821     },
16822     
16823     onTouchViewLoadException : function()
16824     {
16825         this.hideTouchView();
16826     },
16827     
16828     onTouchViewEmptyResults : function()
16829     {
16830         this.clearTouchView();
16831         
16832         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16833         
16834         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16835         
16836     },
16837     
16838     clearTouchView : function()
16839     {
16840         this.touchViewListGroup.dom.innerHTML = '';
16841     },
16842     
16843     onTouchViewClick : function(e, el, o)
16844     {
16845         e.preventDefault();
16846         
16847         var row = o.row;
16848         var rowIndex = o.rowIndex;
16849         
16850         var r = this.store.getAt(rowIndex);
16851         
16852         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16853             
16854             if(!this.multiple){
16855                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16856                     c.dom.removeAttribute('checked');
16857                 }, this);
16858
16859                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16860
16861                 this.setFromData(r.data);
16862
16863                 var close = this.closeTriggerEl();
16864
16865                 if(close){
16866                     close.show();
16867                 }
16868
16869                 this.hideTouchView();
16870
16871                 this.fireEvent('select', this, r, rowIndex);
16872
16873                 return;
16874             }
16875
16876             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16877                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16878                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16879                 return;
16880             }
16881
16882             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16883             this.addItem(r.data);
16884             this.tickItems.push(r.data);
16885         }
16886     },
16887     
16888     getAutoCreateNativeIOS : function()
16889     {
16890         var cfg = {
16891             cls: 'form-group' //input-group,
16892         };
16893         
16894         var combobox =  {
16895             tag: 'select',
16896             cls : 'roo-ios-select'
16897         };
16898         
16899         if (this.name) {
16900             combobox.name = this.name;
16901         }
16902         
16903         if (this.disabled) {
16904             combobox.disabled = true;
16905         }
16906         
16907         var settings = this;
16908         
16909         ['xs','sm','md','lg'].map(function(size){
16910             if (settings[size]) {
16911                 cfg.cls += ' col-' + size + '-' + settings[size];
16912             }
16913         });
16914         
16915         cfg.cn = combobox;
16916         
16917         return cfg;
16918         
16919     },
16920     
16921     initIOSView : function()
16922     {
16923         this.store.on('load', this.onIOSViewLoad, this);
16924         
16925         return;
16926     },
16927     
16928     onIOSViewLoad : function()
16929     {
16930         if(this.store.getCount() < 1){
16931             return;
16932         }
16933         
16934         this.clearIOSView();
16935         
16936         if(this.allowBlank) {
16937             
16938             var default_text = '-- SELECT --';
16939             
16940             if(this.placeholder.length){
16941                 default_text = this.placeholder;
16942             }
16943             
16944             if(this.emptyTitle.length){
16945                 default_text += ' - ' + this.emptyTitle + ' -';
16946             }
16947             
16948             var opt = this.inputEl().createChild({
16949                 tag: 'option',
16950                 value : 0,
16951                 html : default_text
16952             });
16953             
16954             var o = {};
16955             o[this.valueField] = 0;
16956             o[this.displayField] = default_text;
16957             
16958             this.ios_options.push({
16959                 data : o,
16960                 el : opt
16961             });
16962             
16963         }
16964         
16965         this.store.data.each(function(d, rowIndex){
16966             
16967             var html = '';
16968             
16969             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16970                 html = d.data[this.displayField];
16971             }
16972             
16973             var value = '';
16974             
16975             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16976                 value = d.data[this.valueField];
16977             }
16978             
16979             var option = {
16980                 tag: 'option',
16981                 value : value,
16982                 html : html
16983             };
16984             
16985             if(this.value == d.data[this.valueField]){
16986                 option['selected'] = true;
16987             }
16988             
16989             var opt = this.inputEl().createChild(option);
16990             
16991             this.ios_options.push({
16992                 data : d.data,
16993                 el : opt
16994             });
16995             
16996         }, this);
16997         
16998         this.inputEl().on('change', function(){
16999            this.fireEvent('select', this);
17000         }, this);
17001         
17002     },
17003     
17004     clearIOSView: function()
17005     {
17006         this.inputEl().dom.innerHTML = '';
17007         
17008         this.ios_options = [];
17009     },
17010     
17011     setIOSValue: function(v)
17012     {
17013         this.value = v;
17014         
17015         if(!this.ios_options){
17016             return;
17017         }
17018         
17019         Roo.each(this.ios_options, function(opts){
17020            
17021            opts.el.dom.removeAttribute('selected');
17022            
17023            if(opts.data[this.valueField] != v){
17024                return;
17025            }
17026            
17027            opts.el.dom.setAttribute('selected', true);
17028            
17029         }, this);
17030     }
17031
17032     /** 
17033     * @cfg {Boolean} grow 
17034     * @hide 
17035     */
17036     /** 
17037     * @cfg {Number} growMin 
17038     * @hide 
17039     */
17040     /** 
17041     * @cfg {Number} growMax 
17042     * @hide 
17043     */
17044     /**
17045      * @hide
17046      * @method autoSize
17047      */
17048 });
17049
17050 Roo.apply(Roo.bootstrap.ComboBox,  {
17051     
17052     header : {
17053         tag: 'div',
17054         cls: 'modal-header',
17055         cn: [
17056             {
17057                 tag: 'h4',
17058                 cls: 'modal-title'
17059             }
17060         ]
17061     },
17062     
17063     body : {
17064         tag: 'div',
17065         cls: 'modal-body',
17066         cn: [
17067             {
17068                 tag: 'ul',
17069                 cls: 'list-group'
17070             }
17071         ]
17072     },
17073     
17074     listItemRadio : {
17075         tag: 'li',
17076         cls: 'list-group-item',
17077         cn: [
17078             {
17079                 tag: 'span',
17080                 cls: 'roo-combobox-list-group-item-value'
17081             },
17082             {
17083                 tag: 'div',
17084                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17085                 cn: [
17086                     {
17087                         tag: 'input',
17088                         type: 'radio'
17089                     },
17090                     {
17091                         tag: 'label'
17092                     }
17093                 ]
17094             }
17095         ]
17096     },
17097     
17098     listItemCheckbox : {
17099         tag: 'li',
17100         cls: 'list-group-item',
17101         cn: [
17102             {
17103                 tag: 'span',
17104                 cls: 'roo-combobox-list-group-item-value'
17105             },
17106             {
17107                 tag: 'div',
17108                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17109                 cn: [
17110                     {
17111                         tag: 'input',
17112                         type: 'checkbox'
17113                     },
17114                     {
17115                         tag: 'label'
17116                     }
17117                 ]
17118             }
17119         ]
17120     },
17121     
17122     emptyResult : {
17123         tag: 'div',
17124         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17125     },
17126     
17127     footer : {
17128         tag: 'div',
17129         cls: 'modal-footer',
17130         cn: [
17131             {
17132                 tag: 'div',
17133                 cls: 'row',
17134                 cn: [
17135                     {
17136                         tag: 'div',
17137                         cls: 'col-xs-6 text-left',
17138                         cn: {
17139                             tag: 'button',
17140                             cls: 'btn btn-danger roo-touch-view-cancel',
17141                             html: 'Cancel'
17142                         }
17143                     },
17144                     {
17145                         tag: 'div',
17146                         cls: 'col-xs-6 text-right',
17147                         cn: {
17148                             tag: 'button',
17149                             cls: 'btn btn-success roo-touch-view-ok',
17150                             html: 'OK'
17151                         }
17152                     }
17153                 ]
17154             }
17155         ]
17156         
17157     }
17158 });
17159
17160 Roo.apply(Roo.bootstrap.ComboBox,  {
17161     
17162     touchViewTemplate : {
17163         tag: 'div',
17164         cls: 'modal fade roo-combobox-touch-view',
17165         cn: [
17166             {
17167                 tag: 'div',
17168                 cls: 'modal-dialog',
17169                 style : 'position:fixed', // we have to fix position....
17170                 cn: [
17171                     {
17172                         tag: 'div',
17173                         cls: 'modal-content',
17174                         cn: [
17175                             Roo.bootstrap.ComboBox.header,
17176                             Roo.bootstrap.ComboBox.body,
17177                             Roo.bootstrap.ComboBox.footer
17178                         ]
17179                     }
17180                 ]
17181             }
17182         ]
17183     }
17184 });/*
17185  * Based on:
17186  * Ext JS Library 1.1.1
17187  * Copyright(c) 2006-2007, Ext JS, LLC.
17188  *
17189  * Originally Released Under LGPL - original licence link has changed is not relivant.
17190  *
17191  * Fork - LGPL
17192  * <script type="text/javascript">
17193  */
17194
17195 /**
17196  * @class Roo.View
17197  * @extends Roo.util.Observable
17198  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17199  * This class also supports single and multi selection modes. <br>
17200  * Create a data model bound view:
17201  <pre><code>
17202  var store = new Roo.data.Store(...);
17203
17204  var view = new Roo.View({
17205     el : "my-element",
17206     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17207  
17208     singleSelect: true,
17209     selectedClass: "ydataview-selected",
17210     store: store
17211  });
17212
17213  // listen for node click?
17214  view.on("click", function(vw, index, node, e){
17215  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17216  });
17217
17218  // load XML data
17219  dataModel.load("foobar.xml");
17220  </code></pre>
17221  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17222  * <br><br>
17223  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17224  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17225  * 
17226  * Note: old style constructor is still suported (container, template, config)
17227  * 
17228  * @constructor
17229  * Create a new View
17230  * @param {Object} config The config object
17231  * 
17232  */
17233 Roo.View = function(config, depreciated_tpl, depreciated_config){
17234     
17235     this.parent = false;
17236     
17237     if (typeof(depreciated_tpl) == 'undefined') {
17238         // new way.. - universal constructor.
17239         Roo.apply(this, config);
17240         this.el  = Roo.get(this.el);
17241     } else {
17242         // old format..
17243         this.el  = Roo.get(config);
17244         this.tpl = depreciated_tpl;
17245         Roo.apply(this, depreciated_config);
17246     }
17247     this.wrapEl  = this.el.wrap().wrap();
17248     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17249     
17250     
17251     if(typeof(this.tpl) == "string"){
17252         this.tpl = new Roo.Template(this.tpl);
17253     } else {
17254         // support xtype ctors..
17255         this.tpl = new Roo.factory(this.tpl, Roo);
17256     }
17257     
17258     
17259     this.tpl.compile();
17260     
17261     /** @private */
17262     this.addEvents({
17263         /**
17264          * @event beforeclick
17265          * Fires before a click is processed. Returns false to cancel the default action.
17266          * @param {Roo.View} this
17267          * @param {Number} index The index of the target node
17268          * @param {HTMLElement} node The target node
17269          * @param {Roo.EventObject} e The raw event object
17270          */
17271             "beforeclick" : true,
17272         /**
17273          * @event click
17274          * Fires when a template node is clicked.
17275          * @param {Roo.View} this
17276          * @param {Number} index The index of the target node
17277          * @param {HTMLElement} node The target node
17278          * @param {Roo.EventObject} e The raw event object
17279          */
17280             "click" : true,
17281         /**
17282          * @event dblclick
17283          * Fires when a template node is double clicked.
17284          * @param {Roo.View} this
17285          * @param {Number} index The index of the target node
17286          * @param {HTMLElement} node The target node
17287          * @param {Roo.EventObject} e The raw event object
17288          */
17289             "dblclick" : true,
17290         /**
17291          * @event contextmenu
17292          * Fires when a template node is right clicked.
17293          * @param {Roo.View} this
17294          * @param {Number} index The index of the target node
17295          * @param {HTMLElement} node The target node
17296          * @param {Roo.EventObject} e The raw event object
17297          */
17298             "contextmenu" : true,
17299         /**
17300          * @event selectionchange
17301          * Fires when the selected nodes change.
17302          * @param {Roo.View} this
17303          * @param {Array} selections Array of the selected nodes
17304          */
17305             "selectionchange" : true,
17306     
17307         /**
17308          * @event beforeselect
17309          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17310          * @param {Roo.View} this
17311          * @param {HTMLElement} node The node to be selected
17312          * @param {Array} selections Array of currently selected nodes
17313          */
17314             "beforeselect" : true,
17315         /**
17316          * @event preparedata
17317          * Fires on every row to render, to allow you to change the data.
17318          * @param {Roo.View} this
17319          * @param {Object} data to be rendered (change this)
17320          */
17321           "preparedata" : true
17322           
17323           
17324         });
17325
17326
17327
17328     this.el.on({
17329         "click": this.onClick,
17330         "dblclick": this.onDblClick,
17331         "contextmenu": this.onContextMenu,
17332         scope:this
17333     });
17334
17335     this.selections = [];
17336     this.nodes = [];
17337     this.cmp = new Roo.CompositeElementLite([]);
17338     if(this.store){
17339         this.store = Roo.factory(this.store, Roo.data);
17340         this.setStore(this.store, true);
17341     }
17342     
17343     if ( this.footer && this.footer.xtype) {
17344            
17345          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17346         
17347         this.footer.dataSource = this.store;
17348         this.footer.container = fctr;
17349         this.footer = Roo.factory(this.footer, Roo);
17350         fctr.insertFirst(this.el);
17351         
17352         // this is a bit insane - as the paging toolbar seems to detach the el..
17353 //        dom.parentNode.parentNode.parentNode
17354          // they get detached?
17355     }
17356     
17357     
17358     Roo.View.superclass.constructor.call(this);
17359     
17360     
17361 };
17362
17363 Roo.extend(Roo.View, Roo.util.Observable, {
17364     
17365      /**
17366      * @cfg {Roo.data.Store} store Data store to load data from.
17367      */
17368     store : false,
17369     
17370     /**
17371      * @cfg {String|Roo.Element} el The container element.
17372      */
17373     el : '',
17374     
17375     /**
17376      * @cfg {String|Roo.Template} tpl The template used by this View 
17377      */
17378     tpl : false,
17379     /**
17380      * @cfg {String} dataName the named area of the template to use as the data area
17381      *                          Works with domtemplates roo-name="name"
17382      */
17383     dataName: false,
17384     /**
17385      * @cfg {String} selectedClass The css class to add to selected nodes
17386      */
17387     selectedClass : "x-view-selected",
17388      /**
17389      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17390      */
17391     emptyText : "",
17392     
17393     /**
17394      * @cfg {String} text to display on mask (default Loading)
17395      */
17396     mask : false,
17397     /**
17398      * @cfg {Boolean} multiSelect Allow multiple selection
17399      */
17400     multiSelect : false,
17401     /**
17402      * @cfg {Boolean} singleSelect Allow single selection
17403      */
17404     singleSelect:  false,
17405     
17406     /**
17407      * @cfg {Boolean} toggleSelect - selecting 
17408      */
17409     toggleSelect : false,
17410     
17411     /**
17412      * @cfg {Boolean} tickable - selecting 
17413      */
17414     tickable : false,
17415     
17416     /**
17417      * Returns the element this view is bound to.
17418      * @return {Roo.Element}
17419      */
17420     getEl : function(){
17421         return this.wrapEl;
17422     },
17423     
17424     
17425
17426     /**
17427      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17428      */
17429     refresh : function(){
17430         //Roo.log('refresh');
17431         var t = this.tpl;
17432         
17433         // if we are using something like 'domtemplate', then
17434         // the what gets used is:
17435         // t.applySubtemplate(NAME, data, wrapping data..)
17436         // the outer template then get' applied with
17437         //     the store 'extra data'
17438         // and the body get's added to the
17439         //      roo-name="data" node?
17440         //      <span class='roo-tpl-{name}'></span> ?????
17441         
17442         
17443         
17444         this.clearSelections();
17445         this.el.update("");
17446         var html = [];
17447         var records = this.store.getRange();
17448         if(records.length < 1) {
17449             
17450             // is this valid??  = should it render a template??
17451             
17452             this.el.update(this.emptyText);
17453             return;
17454         }
17455         var el = this.el;
17456         if (this.dataName) {
17457             this.el.update(t.apply(this.store.meta)); //????
17458             el = this.el.child('.roo-tpl-' + this.dataName);
17459         }
17460         
17461         for(var i = 0, len = records.length; i < len; i++){
17462             var data = this.prepareData(records[i].data, i, records[i]);
17463             this.fireEvent("preparedata", this, data, i, records[i]);
17464             
17465             var d = Roo.apply({}, data);
17466             
17467             if(this.tickable){
17468                 Roo.apply(d, {'roo-id' : Roo.id()});
17469                 
17470                 var _this = this;
17471             
17472                 Roo.each(this.parent.item, function(item){
17473                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17474                         return;
17475                     }
17476                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17477                 });
17478             }
17479             
17480             html[html.length] = Roo.util.Format.trim(
17481                 this.dataName ?
17482                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17483                     t.apply(d)
17484             );
17485         }
17486         
17487         
17488         
17489         el.update(html.join(""));
17490         this.nodes = el.dom.childNodes;
17491         this.updateIndexes(0);
17492     },
17493     
17494
17495     /**
17496      * Function to override to reformat the data that is sent to
17497      * the template for each node.
17498      * DEPRICATED - use the preparedata event handler.
17499      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17500      * a JSON object for an UpdateManager bound view).
17501      */
17502     prepareData : function(data, index, record)
17503     {
17504         this.fireEvent("preparedata", this, data, index, record);
17505         return data;
17506     },
17507
17508     onUpdate : function(ds, record){
17509         // Roo.log('on update');   
17510         this.clearSelections();
17511         var index = this.store.indexOf(record);
17512         var n = this.nodes[index];
17513         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17514         n.parentNode.removeChild(n);
17515         this.updateIndexes(index, index);
17516     },
17517
17518     
17519     
17520 // --------- FIXME     
17521     onAdd : function(ds, records, index)
17522     {
17523         //Roo.log(['on Add', ds, records, index] );        
17524         this.clearSelections();
17525         if(this.nodes.length == 0){
17526             this.refresh();
17527             return;
17528         }
17529         var n = this.nodes[index];
17530         for(var i = 0, len = records.length; i < len; i++){
17531             var d = this.prepareData(records[i].data, i, records[i]);
17532             if(n){
17533                 this.tpl.insertBefore(n, d);
17534             }else{
17535                 
17536                 this.tpl.append(this.el, d);
17537             }
17538         }
17539         this.updateIndexes(index);
17540     },
17541
17542     onRemove : function(ds, record, index){
17543        // Roo.log('onRemove');
17544         this.clearSelections();
17545         var el = this.dataName  ?
17546             this.el.child('.roo-tpl-' + this.dataName) :
17547             this.el; 
17548         
17549         el.dom.removeChild(this.nodes[index]);
17550         this.updateIndexes(index);
17551     },
17552
17553     /**
17554      * Refresh an individual node.
17555      * @param {Number} index
17556      */
17557     refreshNode : function(index){
17558         this.onUpdate(this.store, this.store.getAt(index));
17559     },
17560
17561     updateIndexes : function(startIndex, endIndex){
17562         var ns = this.nodes;
17563         startIndex = startIndex || 0;
17564         endIndex = endIndex || ns.length - 1;
17565         for(var i = startIndex; i <= endIndex; i++){
17566             ns[i].nodeIndex = i;
17567         }
17568     },
17569
17570     /**
17571      * Changes the data store this view uses and refresh the view.
17572      * @param {Store} store
17573      */
17574     setStore : function(store, initial){
17575         if(!initial && this.store){
17576             this.store.un("datachanged", this.refresh);
17577             this.store.un("add", this.onAdd);
17578             this.store.un("remove", this.onRemove);
17579             this.store.un("update", this.onUpdate);
17580             this.store.un("clear", this.refresh);
17581             this.store.un("beforeload", this.onBeforeLoad);
17582             this.store.un("load", this.onLoad);
17583             this.store.un("loadexception", this.onLoad);
17584         }
17585         if(store){
17586           
17587             store.on("datachanged", this.refresh, this);
17588             store.on("add", this.onAdd, this);
17589             store.on("remove", this.onRemove, this);
17590             store.on("update", this.onUpdate, this);
17591             store.on("clear", this.refresh, this);
17592             store.on("beforeload", this.onBeforeLoad, this);
17593             store.on("load", this.onLoad, this);
17594             store.on("loadexception", this.onLoad, this);
17595         }
17596         
17597         if(store){
17598             this.refresh();
17599         }
17600     },
17601     /**
17602      * onbeforeLoad - masks the loading area.
17603      *
17604      */
17605     onBeforeLoad : function(store,opts)
17606     {
17607          //Roo.log('onBeforeLoad');   
17608         if (!opts.add) {
17609             this.el.update("");
17610         }
17611         this.el.mask(this.mask ? this.mask : "Loading" ); 
17612     },
17613     onLoad : function ()
17614     {
17615         this.el.unmask();
17616     },
17617     
17618
17619     /**
17620      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17621      * @param {HTMLElement} node
17622      * @return {HTMLElement} The template node
17623      */
17624     findItemFromChild : function(node){
17625         var el = this.dataName  ?
17626             this.el.child('.roo-tpl-' + this.dataName,true) :
17627             this.el.dom; 
17628         
17629         if(!node || node.parentNode == el){
17630                     return node;
17631             }
17632             var p = node.parentNode;
17633             while(p && p != el){
17634             if(p.parentNode == el){
17635                 return p;
17636             }
17637             p = p.parentNode;
17638         }
17639             return null;
17640     },
17641
17642     /** @ignore */
17643     onClick : function(e){
17644         var item = this.findItemFromChild(e.getTarget());
17645         if(item){
17646             var index = this.indexOf(item);
17647             if(this.onItemClick(item, index, e) !== false){
17648                 this.fireEvent("click", this, index, item, e);
17649             }
17650         }else{
17651             this.clearSelections();
17652         }
17653     },
17654
17655     /** @ignore */
17656     onContextMenu : function(e){
17657         var item = this.findItemFromChild(e.getTarget());
17658         if(item){
17659             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17660         }
17661     },
17662
17663     /** @ignore */
17664     onDblClick : function(e){
17665         var item = this.findItemFromChild(e.getTarget());
17666         if(item){
17667             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17668         }
17669     },
17670
17671     onItemClick : function(item, index, e)
17672     {
17673         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17674             return false;
17675         }
17676         if (this.toggleSelect) {
17677             var m = this.isSelected(item) ? 'unselect' : 'select';
17678             //Roo.log(m);
17679             var _t = this;
17680             _t[m](item, true, false);
17681             return true;
17682         }
17683         if(this.multiSelect || this.singleSelect){
17684             if(this.multiSelect && e.shiftKey && this.lastSelection){
17685                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17686             }else{
17687                 this.select(item, this.multiSelect && e.ctrlKey);
17688                 this.lastSelection = item;
17689             }
17690             
17691             if(!this.tickable){
17692                 e.preventDefault();
17693             }
17694             
17695         }
17696         return true;
17697     },
17698
17699     /**
17700      * Get the number of selected nodes.
17701      * @return {Number}
17702      */
17703     getSelectionCount : function(){
17704         return this.selections.length;
17705     },
17706
17707     /**
17708      * Get the currently selected nodes.
17709      * @return {Array} An array of HTMLElements
17710      */
17711     getSelectedNodes : function(){
17712         return this.selections;
17713     },
17714
17715     /**
17716      * Get the indexes of the selected nodes.
17717      * @return {Array}
17718      */
17719     getSelectedIndexes : function(){
17720         var indexes = [], s = this.selections;
17721         for(var i = 0, len = s.length; i < len; i++){
17722             indexes.push(s[i].nodeIndex);
17723         }
17724         return indexes;
17725     },
17726
17727     /**
17728      * Clear all selections
17729      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17730      */
17731     clearSelections : function(suppressEvent){
17732         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17733             this.cmp.elements = this.selections;
17734             this.cmp.removeClass(this.selectedClass);
17735             this.selections = [];
17736             if(!suppressEvent){
17737                 this.fireEvent("selectionchange", this, this.selections);
17738             }
17739         }
17740     },
17741
17742     /**
17743      * Returns true if the passed node is selected
17744      * @param {HTMLElement/Number} node The node or node index
17745      * @return {Boolean}
17746      */
17747     isSelected : function(node){
17748         var s = this.selections;
17749         if(s.length < 1){
17750             return false;
17751         }
17752         node = this.getNode(node);
17753         return s.indexOf(node) !== -1;
17754     },
17755
17756     /**
17757      * Selects nodes.
17758      * @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
17759      * @param {Boolean} keepExisting (optional) true to keep existing selections
17760      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17761      */
17762     select : function(nodeInfo, keepExisting, suppressEvent){
17763         if(nodeInfo instanceof Array){
17764             if(!keepExisting){
17765                 this.clearSelections(true);
17766             }
17767             for(var i = 0, len = nodeInfo.length; i < len; i++){
17768                 this.select(nodeInfo[i], true, true);
17769             }
17770             return;
17771         } 
17772         var node = this.getNode(nodeInfo);
17773         if(!node || this.isSelected(node)){
17774             return; // already selected.
17775         }
17776         if(!keepExisting){
17777             this.clearSelections(true);
17778         }
17779         
17780         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17781             Roo.fly(node).addClass(this.selectedClass);
17782             this.selections.push(node);
17783             if(!suppressEvent){
17784                 this.fireEvent("selectionchange", this, this.selections);
17785             }
17786         }
17787         
17788         
17789     },
17790       /**
17791      * Unselects nodes.
17792      * @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
17793      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17794      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17795      */
17796     unselect : function(nodeInfo, keepExisting, suppressEvent)
17797     {
17798         if(nodeInfo instanceof Array){
17799             Roo.each(this.selections, function(s) {
17800                 this.unselect(s, nodeInfo);
17801             }, this);
17802             return;
17803         }
17804         var node = this.getNode(nodeInfo);
17805         if(!node || !this.isSelected(node)){
17806             //Roo.log("not selected");
17807             return; // not selected.
17808         }
17809         // fireevent???
17810         var ns = [];
17811         Roo.each(this.selections, function(s) {
17812             if (s == node ) {
17813                 Roo.fly(node).removeClass(this.selectedClass);
17814
17815                 return;
17816             }
17817             ns.push(s);
17818         },this);
17819         
17820         this.selections= ns;
17821         this.fireEvent("selectionchange", this, this.selections);
17822     },
17823
17824     /**
17825      * Gets a template node.
17826      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17827      * @return {HTMLElement} The node or null if it wasn't found
17828      */
17829     getNode : function(nodeInfo){
17830         if(typeof nodeInfo == "string"){
17831             return document.getElementById(nodeInfo);
17832         }else if(typeof nodeInfo == "number"){
17833             return this.nodes[nodeInfo];
17834         }
17835         return nodeInfo;
17836     },
17837
17838     /**
17839      * Gets a range template nodes.
17840      * @param {Number} startIndex
17841      * @param {Number} endIndex
17842      * @return {Array} An array of nodes
17843      */
17844     getNodes : function(start, end){
17845         var ns = this.nodes;
17846         start = start || 0;
17847         end = typeof end == "undefined" ? ns.length - 1 : end;
17848         var nodes = [];
17849         if(start <= end){
17850             for(var i = start; i <= end; i++){
17851                 nodes.push(ns[i]);
17852             }
17853         } else{
17854             for(var i = start; i >= end; i--){
17855                 nodes.push(ns[i]);
17856             }
17857         }
17858         return nodes;
17859     },
17860
17861     /**
17862      * Finds the index of the passed node
17863      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17864      * @return {Number} The index of the node or -1
17865      */
17866     indexOf : function(node){
17867         node = this.getNode(node);
17868         if(typeof node.nodeIndex == "number"){
17869             return node.nodeIndex;
17870         }
17871         var ns = this.nodes;
17872         for(var i = 0, len = ns.length; i < len; i++){
17873             if(ns[i] == node){
17874                 return i;
17875             }
17876         }
17877         return -1;
17878     }
17879 });
17880 /*
17881  * - LGPL
17882  *
17883  * based on jquery fullcalendar
17884  * 
17885  */
17886
17887 Roo.bootstrap = Roo.bootstrap || {};
17888 /**
17889  * @class Roo.bootstrap.Calendar
17890  * @extends Roo.bootstrap.Component
17891  * Bootstrap Calendar class
17892  * @cfg {Boolean} loadMask (true|false) default false
17893  * @cfg {Object} header generate the user specific header of the calendar, default false
17894
17895  * @constructor
17896  * Create a new Container
17897  * @param {Object} config The config object
17898  */
17899
17900
17901
17902 Roo.bootstrap.Calendar = function(config){
17903     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17904      this.addEvents({
17905         /**
17906              * @event select
17907              * Fires when a date is selected
17908              * @param {DatePicker} this
17909              * @param {Date} date The selected date
17910              */
17911         'select': true,
17912         /**
17913              * @event monthchange
17914              * Fires when the displayed month changes 
17915              * @param {DatePicker} this
17916              * @param {Date} date The selected month
17917              */
17918         'monthchange': true,
17919         /**
17920              * @event evententer
17921              * Fires when mouse over an event
17922              * @param {Calendar} this
17923              * @param {event} Event
17924              */
17925         'evententer': true,
17926         /**
17927              * @event eventleave
17928              * Fires when the mouse leaves an
17929              * @param {Calendar} this
17930              * @param {event}
17931              */
17932         'eventleave': true,
17933         /**
17934              * @event eventclick
17935              * Fires when the mouse click an
17936              * @param {Calendar} this
17937              * @param {event}
17938              */
17939         'eventclick': true
17940         
17941     });
17942
17943 };
17944
17945 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17946     
17947      /**
17948      * @cfg {Number} startDay
17949      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17950      */
17951     startDay : 0,
17952     
17953     loadMask : false,
17954     
17955     header : false,
17956       
17957     getAutoCreate : function(){
17958         
17959         
17960         var fc_button = function(name, corner, style, content ) {
17961             return Roo.apply({},{
17962                 tag : 'span',
17963                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17964                          (corner.length ?
17965                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17966                             ''
17967                         ),
17968                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17969                 unselectable: 'on'
17970             });
17971         };
17972         
17973         var header = {};
17974         
17975         if(!this.header){
17976             header = {
17977                 tag : 'table',
17978                 cls : 'fc-header',
17979                 style : 'width:100%',
17980                 cn : [
17981                     {
17982                         tag: 'tr',
17983                         cn : [
17984                             {
17985                                 tag : 'td',
17986                                 cls : 'fc-header-left',
17987                                 cn : [
17988                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17989                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17990                                     { tag: 'span', cls: 'fc-header-space' },
17991                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17992
17993
17994                                 ]
17995                             },
17996
17997                             {
17998                                 tag : 'td',
17999                                 cls : 'fc-header-center',
18000                                 cn : [
18001                                     {
18002                                         tag: 'span',
18003                                         cls: 'fc-header-title',
18004                                         cn : {
18005                                             tag: 'H2',
18006                                             html : 'month / year'
18007                                         }
18008                                     }
18009
18010                                 ]
18011                             },
18012                             {
18013                                 tag : 'td',
18014                                 cls : 'fc-header-right',
18015                                 cn : [
18016                               /*      fc_button('month', 'left', '', 'month' ),
18017                                     fc_button('week', '', '', 'week' ),
18018                                     fc_button('day', 'right', '', 'day' )
18019                                 */    
18020
18021                                 ]
18022                             }
18023
18024                         ]
18025                     }
18026                 ]
18027             };
18028         }
18029         
18030         header = this.header;
18031         
18032        
18033         var cal_heads = function() {
18034             var ret = [];
18035             // fixme - handle this.
18036             
18037             for (var i =0; i < Date.dayNames.length; i++) {
18038                 var d = Date.dayNames[i];
18039                 ret.push({
18040                     tag: 'th',
18041                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18042                     html : d.substring(0,3)
18043                 });
18044                 
18045             }
18046             ret[0].cls += ' fc-first';
18047             ret[6].cls += ' fc-last';
18048             return ret;
18049         };
18050         var cal_cell = function(n) {
18051             return  {
18052                 tag: 'td',
18053                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18054                 cn : [
18055                     {
18056                         cn : [
18057                             {
18058                                 cls: 'fc-day-number',
18059                                 html: 'D'
18060                             },
18061                             {
18062                                 cls: 'fc-day-content',
18063                              
18064                                 cn : [
18065                                      {
18066                                         style: 'position: relative;' // height: 17px;
18067                                     }
18068                                 ]
18069                             }
18070                             
18071                             
18072                         ]
18073                     }
18074                 ]
18075                 
18076             }
18077         };
18078         var cal_rows = function() {
18079             
18080             var ret = [];
18081             for (var r = 0; r < 6; r++) {
18082                 var row= {
18083                     tag : 'tr',
18084                     cls : 'fc-week',
18085                     cn : []
18086                 };
18087                 
18088                 for (var i =0; i < Date.dayNames.length; i++) {
18089                     var d = Date.dayNames[i];
18090                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18091
18092                 }
18093                 row.cn[0].cls+=' fc-first';
18094                 row.cn[0].cn[0].style = 'min-height:90px';
18095                 row.cn[6].cls+=' fc-last';
18096                 ret.push(row);
18097                 
18098             }
18099             ret[0].cls += ' fc-first';
18100             ret[4].cls += ' fc-prev-last';
18101             ret[5].cls += ' fc-last';
18102             return ret;
18103             
18104         };
18105         
18106         var cal_table = {
18107             tag: 'table',
18108             cls: 'fc-border-separate',
18109             style : 'width:100%',
18110             cellspacing  : 0,
18111             cn : [
18112                 { 
18113                     tag: 'thead',
18114                     cn : [
18115                         { 
18116                             tag: 'tr',
18117                             cls : 'fc-first fc-last',
18118                             cn : cal_heads()
18119                         }
18120                     ]
18121                 },
18122                 { 
18123                     tag: 'tbody',
18124                     cn : cal_rows()
18125                 }
18126                   
18127             ]
18128         };
18129          
18130          var cfg = {
18131             cls : 'fc fc-ltr',
18132             cn : [
18133                 header,
18134                 {
18135                     cls : 'fc-content',
18136                     style : "position: relative;",
18137                     cn : [
18138                         {
18139                             cls : 'fc-view fc-view-month fc-grid',
18140                             style : 'position: relative',
18141                             unselectable : 'on',
18142                             cn : [
18143                                 {
18144                                     cls : 'fc-event-container',
18145                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18146                                 },
18147                                 cal_table
18148                             ]
18149                         }
18150                     ]
18151     
18152                 }
18153            ] 
18154             
18155         };
18156         
18157          
18158         
18159         return cfg;
18160     },
18161     
18162     
18163     initEvents : function()
18164     {
18165         if(!this.store){
18166             throw "can not find store for calendar";
18167         }
18168         
18169         var mark = {
18170             tag: "div",
18171             cls:"x-dlg-mask",
18172             style: "text-align:center",
18173             cn: [
18174                 {
18175                     tag: "div",
18176                     style: "background-color:white;width:50%;margin:250 auto",
18177                     cn: [
18178                         {
18179                             tag: "img",
18180                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18181                         },
18182                         {
18183                             tag: "span",
18184                             html: "Loading"
18185                         }
18186                         
18187                     ]
18188                 }
18189             ]
18190         };
18191         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18192         
18193         var size = this.el.select('.fc-content', true).first().getSize();
18194         this.maskEl.setSize(size.width, size.height);
18195         this.maskEl.enableDisplayMode("block");
18196         if(!this.loadMask){
18197             this.maskEl.hide();
18198         }
18199         
18200         this.store = Roo.factory(this.store, Roo.data);
18201         this.store.on('load', this.onLoad, this);
18202         this.store.on('beforeload', this.onBeforeLoad, this);
18203         
18204         this.resize();
18205         
18206         this.cells = this.el.select('.fc-day',true);
18207         //Roo.log(this.cells);
18208         this.textNodes = this.el.query('.fc-day-number');
18209         this.cells.addClassOnOver('fc-state-hover');
18210         
18211         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18212         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18213         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18214         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18215         
18216         this.on('monthchange', this.onMonthChange, this);
18217         
18218         this.update(new Date().clearTime());
18219     },
18220     
18221     resize : function() {
18222         var sz  = this.el.getSize();
18223         
18224         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18225         this.el.select('.fc-day-content div',true).setHeight(34);
18226     },
18227     
18228     
18229     // private
18230     showPrevMonth : function(e){
18231         this.update(this.activeDate.add("mo", -1));
18232     },
18233     showToday : function(e){
18234         this.update(new Date().clearTime());
18235     },
18236     // private
18237     showNextMonth : function(e){
18238         this.update(this.activeDate.add("mo", 1));
18239     },
18240
18241     // private
18242     showPrevYear : function(){
18243         this.update(this.activeDate.add("y", -1));
18244     },
18245
18246     // private
18247     showNextYear : function(){
18248         this.update(this.activeDate.add("y", 1));
18249     },
18250
18251     
18252    // private
18253     update : function(date)
18254     {
18255         var vd = this.activeDate;
18256         this.activeDate = date;
18257 //        if(vd && this.el){
18258 //            var t = date.getTime();
18259 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18260 //                Roo.log('using add remove');
18261 //                
18262 //                this.fireEvent('monthchange', this, date);
18263 //                
18264 //                this.cells.removeClass("fc-state-highlight");
18265 //                this.cells.each(function(c){
18266 //                   if(c.dateValue == t){
18267 //                       c.addClass("fc-state-highlight");
18268 //                       setTimeout(function(){
18269 //                            try{c.dom.firstChild.focus();}catch(e){}
18270 //                       }, 50);
18271 //                       return false;
18272 //                   }
18273 //                   return true;
18274 //                });
18275 //                return;
18276 //            }
18277 //        }
18278         
18279         var days = date.getDaysInMonth();
18280         
18281         var firstOfMonth = date.getFirstDateOfMonth();
18282         var startingPos = firstOfMonth.getDay()-this.startDay;
18283         
18284         if(startingPos < this.startDay){
18285             startingPos += 7;
18286         }
18287         
18288         var pm = date.add(Date.MONTH, -1);
18289         var prevStart = pm.getDaysInMonth()-startingPos;
18290 //        
18291         this.cells = this.el.select('.fc-day',true);
18292         this.textNodes = this.el.query('.fc-day-number');
18293         this.cells.addClassOnOver('fc-state-hover');
18294         
18295         var cells = this.cells.elements;
18296         var textEls = this.textNodes;
18297         
18298         Roo.each(cells, function(cell){
18299             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18300         });
18301         
18302         days += startingPos;
18303
18304         // convert everything to numbers so it's fast
18305         var day = 86400000;
18306         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18307         //Roo.log(d);
18308         //Roo.log(pm);
18309         //Roo.log(prevStart);
18310         
18311         var today = new Date().clearTime().getTime();
18312         var sel = date.clearTime().getTime();
18313         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18314         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18315         var ddMatch = this.disabledDatesRE;
18316         var ddText = this.disabledDatesText;
18317         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18318         var ddaysText = this.disabledDaysText;
18319         var format = this.format;
18320         
18321         var setCellClass = function(cal, cell){
18322             cell.row = 0;
18323             cell.events = [];
18324             cell.more = [];
18325             //Roo.log('set Cell Class');
18326             cell.title = "";
18327             var t = d.getTime();
18328             
18329             //Roo.log(d);
18330             
18331             cell.dateValue = t;
18332             if(t == today){
18333                 cell.className += " fc-today";
18334                 cell.className += " fc-state-highlight";
18335                 cell.title = cal.todayText;
18336             }
18337             if(t == sel){
18338                 // disable highlight in other month..
18339                 //cell.className += " fc-state-highlight";
18340                 
18341             }
18342             // disabling
18343             if(t < min) {
18344                 cell.className = " fc-state-disabled";
18345                 cell.title = cal.minText;
18346                 return;
18347             }
18348             if(t > max) {
18349                 cell.className = " fc-state-disabled";
18350                 cell.title = cal.maxText;
18351                 return;
18352             }
18353             if(ddays){
18354                 if(ddays.indexOf(d.getDay()) != -1){
18355                     cell.title = ddaysText;
18356                     cell.className = " fc-state-disabled";
18357                 }
18358             }
18359             if(ddMatch && format){
18360                 var fvalue = d.dateFormat(format);
18361                 if(ddMatch.test(fvalue)){
18362                     cell.title = ddText.replace("%0", fvalue);
18363                     cell.className = " fc-state-disabled";
18364                 }
18365             }
18366             
18367             if (!cell.initialClassName) {
18368                 cell.initialClassName = cell.dom.className;
18369             }
18370             
18371             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18372         };
18373
18374         var i = 0;
18375         
18376         for(; i < startingPos; i++) {
18377             textEls[i].innerHTML = (++prevStart);
18378             d.setDate(d.getDate()+1);
18379             
18380             cells[i].className = "fc-past fc-other-month";
18381             setCellClass(this, cells[i]);
18382         }
18383         
18384         var intDay = 0;
18385         
18386         for(; i < days; i++){
18387             intDay = i - startingPos + 1;
18388             textEls[i].innerHTML = (intDay);
18389             d.setDate(d.getDate()+1);
18390             
18391             cells[i].className = ''; // "x-date-active";
18392             setCellClass(this, cells[i]);
18393         }
18394         var extraDays = 0;
18395         
18396         for(; i < 42; i++) {
18397             textEls[i].innerHTML = (++extraDays);
18398             d.setDate(d.getDate()+1);
18399             
18400             cells[i].className = "fc-future fc-other-month";
18401             setCellClass(this, cells[i]);
18402         }
18403         
18404         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18405         
18406         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18407         
18408         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18409         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18410         
18411         if(totalRows != 6){
18412             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18413             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18414         }
18415         
18416         this.fireEvent('monthchange', this, date);
18417         
18418         
18419         /*
18420         if(!this.internalRender){
18421             var main = this.el.dom.firstChild;
18422             var w = main.offsetWidth;
18423             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18424             Roo.fly(main).setWidth(w);
18425             this.internalRender = true;
18426             // opera does not respect the auto grow header center column
18427             // then, after it gets a width opera refuses to recalculate
18428             // without a second pass
18429             if(Roo.isOpera && !this.secondPass){
18430                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18431                 this.secondPass = true;
18432                 this.update.defer(10, this, [date]);
18433             }
18434         }
18435         */
18436         
18437     },
18438     
18439     findCell : function(dt) {
18440         dt = dt.clearTime().getTime();
18441         var ret = false;
18442         this.cells.each(function(c){
18443             //Roo.log("check " +c.dateValue + '?=' + dt);
18444             if(c.dateValue == dt){
18445                 ret = c;
18446                 return false;
18447             }
18448             return true;
18449         });
18450         
18451         return ret;
18452     },
18453     
18454     findCells : function(ev) {
18455         var s = ev.start.clone().clearTime().getTime();
18456        // Roo.log(s);
18457         var e= ev.end.clone().clearTime().getTime();
18458        // Roo.log(e);
18459         var ret = [];
18460         this.cells.each(function(c){
18461              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18462             
18463             if(c.dateValue > e){
18464                 return ;
18465             }
18466             if(c.dateValue < s){
18467                 return ;
18468             }
18469             ret.push(c);
18470         });
18471         
18472         return ret;    
18473     },
18474     
18475 //    findBestRow: function(cells)
18476 //    {
18477 //        var ret = 0;
18478 //        
18479 //        for (var i =0 ; i < cells.length;i++) {
18480 //            ret  = Math.max(cells[i].rows || 0,ret);
18481 //        }
18482 //        return ret;
18483 //        
18484 //    },
18485     
18486     
18487     addItem : function(ev)
18488     {
18489         // look for vertical location slot in
18490         var cells = this.findCells(ev);
18491         
18492 //        ev.row = this.findBestRow(cells);
18493         
18494         // work out the location.
18495         
18496         var crow = false;
18497         var rows = [];
18498         for(var i =0; i < cells.length; i++) {
18499             
18500             cells[i].row = cells[0].row;
18501             
18502             if(i == 0){
18503                 cells[i].row = cells[i].row + 1;
18504             }
18505             
18506             if (!crow) {
18507                 crow = {
18508                     start : cells[i],
18509                     end :  cells[i]
18510                 };
18511                 continue;
18512             }
18513             if (crow.start.getY() == cells[i].getY()) {
18514                 // on same row.
18515                 crow.end = cells[i];
18516                 continue;
18517             }
18518             // different row.
18519             rows.push(crow);
18520             crow = {
18521                 start: cells[i],
18522                 end : cells[i]
18523             };
18524             
18525         }
18526         
18527         rows.push(crow);
18528         ev.els = [];
18529         ev.rows = rows;
18530         ev.cells = cells;
18531         
18532         cells[0].events.push(ev);
18533         
18534         this.calevents.push(ev);
18535     },
18536     
18537     clearEvents: function() {
18538         
18539         if(!this.calevents){
18540             return;
18541         }
18542         
18543         Roo.each(this.cells.elements, function(c){
18544             c.row = 0;
18545             c.events = [];
18546             c.more = [];
18547         });
18548         
18549         Roo.each(this.calevents, function(e) {
18550             Roo.each(e.els, function(el) {
18551                 el.un('mouseenter' ,this.onEventEnter, this);
18552                 el.un('mouseleave' ,this.onEventLeave, this);
18553                 el.remove();
18554             },this);
18555         },this);
18556         
18557         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18558             e.remove();
18559         });
18560         
18561     },
18562     
18563     renderEvents: function()
18564     {   
18565         var _this = this;
18566         
18567         this.cells.each(function(c) {
18568             
18569             if(c.row < 5){
18570                 return;
18571             }
18572             
18573             var ev = c.events;
18574             
18575             var r = 4;
18576             if(c.row != c.events.length){
18577                 r = 4 - (4 - (c.row - c.events.length));
18578             }
18579             
18580             c.events = ev.slice(0, r);
18581             c.more = ev.slice(r);
18582             
18583             if(c.more.length && c.more.length == 1){
18584                 c.events.push(c.more.pop());
18585             }
18586             
18587             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18588             
18589         });
18590             
18591         this.cells.each(function(c) {
18592             
18593             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18594             
18595             
18596             for (var e = 0; e < c.events.length; e++){
18597                 var ev = c.events[e];
18598                 var rows = ev.rows;
18599                 
18600                 for(var i = 0; i < rows.length; i++) {
18601                 
18602                     // how many rows should it span..
18603
18604                     var  cfg = {
18605                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18606                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18607
18608                         unselectable : "on",
18609                         cn : [
18610                             {
18611                                 cls: 'fc-event-inner',
18612                                 cn : [
18613     //                                {
18614     //                                  tag:'span',
18615     //                                  cls: 'fc-event-time',
18616     //                                  html : cells.length > 1 ? '' : ev.time
18617     //                                },
18618                                     {
18619                                       tag:'span',
18620                                       cls: 'fc-event-title',
18621                                       html : String.format('{0}', ev.title)
18622                                     }
18623
18624
18625                                 ]
18626                             },
18627                             {
18628                                 cls: 'ui-resizable-handle ui-resizable-e',
18629                                 html : '&nbsp;&nbsp;&nbsp'
18630                             }
18631
18632                         ]
18633                     };
18634
18635                     if (i == 0) {
18636                         cfg.cls += ' fc-event-start';
18637                     }
18638                     if ((i+1) == rows.length) {
18639                         cfg.cls += ' fc-event-end';
18640                     }
18641
18642                     var ctr = _this.el.select('.fc-event-container',true).first();
18643                     var cg = ctr.createChild(cfg);
18644
18645                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18646                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18647
18648                     var r = (c.more.length) ? 1 : 0;
18649                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18650                     cg.setWidth(ebox.right - sbox.x -2);
18651
18652                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18653                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18654                     cg.on('click', _this.onEventClick, _this, ev);
18655
18656                     ev.els.push(cg);
18657                     
18658                 }
18659                 
18660             }
18661             
18662             
18663             if(c.more.length){
18664                 var  cfg = {
18665                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18666                     style : 'position: absolute',
18667                     unselectable : "on",
18668                     cn : [
18669                         {
18670                             cls: 'fc-event-inner',
18671                             cn : [
18672                                 {
18673                                   tag:'span',
18674                                   cls: 'fc-event-title',
18675                                   html : 'More'
18676                                 }
18677
18678
18679                             ]
18680                         },
18681                         {
18682                             cls: 'ui-resizable-handle ui-resizable-e',
18683                             html : '&nbsp;&nbsp;&nbsp'
18684                         }
18685
18686                     ]
18687                 };
18688
18689                 var ctr = _this.el.select('.fc-event-container',true).first();
18690                 var cg = ctr.createChild(cfg);
18691
18692                 var sbox = c.select('.fc-day-content',true).first().getBox();
18693                 var ebox = c.select('.fc-day-content',true).first().getBox();
18694                 //Roo.log(cg);
18695                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18696                 cg.setWidth(ebox.right - sbox.x -2);
18697
18698                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18699                 
18700             }
18701             
18702         });
18703         
18704         
18705         
18706     },
18707     
18708     onEventEnter: function (e, el,event,d) {
18709         this.fireEvent('evententer', this, el, event);
18710     },
18711     
18712     onEventLeave: function (e, el,event,d) {
18713         this.fireEvent('eventleave', this, el, event);
18714     },
18715     
18716     onEventClick: function (e, el,event,d) {
18717         this.fireEvent('eventclick', this, el, event);
18718     },
18719     
18720     onMonthChange: function () {
18721         this.store.load();
18722     },
18723     
18724     onMoreEventClick: function(e, el, more)
18725     {
18726         var _this = this;
18727         
18728         this.calpopover.placement = 'right';
18729         this.calpopover.setTitle('More');
18730         
18731         this.calpopover.setContent('');
18732         
18733         var ctr = this.calpopover.el.select('.popover-content', true).first();
18734         
18735         Roo.each(more, function(m){
18736             var cfg = {
18737                 cls : 'fc-event-hori fc-event-draggable',
18738                 html : m.title
18739             };
18740             var cg = ctr.createChild(cfg);
18741             
18742             cg.on('click', _this.onEventClick, _this, m);
18743         });
18744         
18745         this.calpopover.show(el);
18746         
18747         
18748     },
18749     
18750     onLoad: function () 
18751     {   
18752         this.calevents = [];
18753         var cal = this;
18754         
18755         if(this.store.getCount() > 0){
18756             this.store.data.each(function(d){
18757                cal.addItem({
18758                     id : d.data.id,
18759                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18760                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18761                     time : d.data.start_time,
18762                     title : d.data.title,
18763                     description : d.data.description,
18764                     venue : d.data.venue
18765                 });
18766             });
18767         }
18768         
18769         this.renderEvents();
18770         
18771         if(this.calevents.length && this.loadMask){
18772             this.maskEl.hide();
18773         }
18774     },
18775     
18776     onBeforeLoad: function()
18777     {
18778         this.clearEvents();
18779         if(this.loadMask){
18780             this.maskEl.show();
18781         }
18782     }
18783 });
18784
18785  
18786  /*
18787  * - LGPL
18788  *
18789  * element
18790  * 
18791  */
18792
18793 /**
18794  * @class Roo.bootstrap.Popover
18795  * @extends Roo.bootstrap.Component
18796  * Bootstrap Popover class
18797  * @cfg {String} html contents of the popover   (or false to use children..)
18798  * @cfg {String} title of popover (or false to hide)
18799  * @cfg {String} placement how it is placed
18800  * @cfg {String} trigger click || hover (or false to trigger manually)
18801  * @cfg {String} over what (parent or false to trigger manually.)
18802  * @cfg {Number} delay - delay before showing
18803  
18804  * @constructor
18805  * Create a new Popover
18806  * @param {Object} config The config object
18807  */
18808
18809 Roo.bootstrap.Popover = function(config){
18810     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18811     
18812     this.addEvents({
18813         // raw events
18814          /**
18815          * @event show
18816          * After the popover show
18817          * 
18818          * @param {Roo.bootstrap.Popover} this
18819          */
18820         "show" : true,
18821         /**
18822          * @event hide
18823          * After the popover hide
18824          * 
18825          * @param {Roo.bootstrap.Popover} this
18826          */
18827         "hide" : true
18828     });
18829 };
18830
18831 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18832     
18833     title: 'Fill in a title',
18834     html: false,
18835     
18836     placement : 'right',
18837     trigger : 'hover', // hover
18838     
18839     delay : 0,
18840     
18841     over: 'parent',
18842     
18843     can_build_overlaid : false,
18844     
18845     getChildContainer : function()
18846     {
18847         return this.el.select('.popover-content',true).first();
18848     },
18849     
18850     getAutoCreate : function(){
18851          
18852         var cfg = {
18853            cls : 'popover roo-dynamic',
18854            style: 'display:block',
18855            cn : [
18856                 {
18857                     cls : 'arrow'
18858                 },
18859                 {
18860                     cls : 'popover-inner',
18861                     cn : [
18862                         {
18863                             tag: 'h3',
18864                             cls: 'popover-title popover-header',
18865                             html : this.title
18866                         },
18867                         {
18868                             cls : 'popover-content popover-body',
18869                             html : this.html
18870                         }
18871                     ]
18872                     
18873                 }
18874            ]
18875         };
18876         
18877         return cfg;
18878     },
18879     setTitle: function(str)
18880     {
18881         this.title = str;
18882         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18883     },
18884     setContent: function(str)
18885     {
18886         this.html = str;
18887         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18888     },
18889     // as it get's added to the bottom of the page.
18890     onRender : function(ct, position)
18891     {
18892         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18893         if(!this.el){
18894             var cfg = Roo.apply({},  this.getAutoCreate());
18895             cfg.id = Roo.id();
18896             
18897             if (this.cls) {
18898                 cfg.cls += ' ' + this.cls;
18899             }
18900             if (this.style) {
18901                 cfg.style = this.style;
18902             }
18903             //Roo.log("adding to ");
18904             this.el = Roo.get(document.body).createChild(cfg, position);
18905 //            Roo.log(this.el);
18906         }
18907         this.initEvents();
18908     },
18909     
18910     initEvents : function()
18911     {
18912         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18913         this.el.enableDisplayMode('block');
18914         this.el.hide();
18915         if (this.over === false) {
18916             return; 
18917         }
18918         if (this.triggers === false) {
18919             return;
18920         }
18921         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18922         var triggers = this.trigger ? this.trigger.split(' ') : [];
18923         Roo.each(triggers, function(trigger) {
18924         
18925             if (trigger == 'click') {
18926                 on_el.on('click', this.toggle, this);
18927             } else if (trigger != 'manual') {
18928                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18929                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18930       
18931                 on_el.on(eventIn  ,this.enter, this);
18932                 on_el.on(eventOut, this.leave, this);
18933             }
18934         }, this);
18935         
18936     },
18937     
18938     
18939     // private
18940     timeout : null,
18941     hoverState : null,
18942     
18943     toggle : function () {
18944         this.hoverState == 'in' ? this.leave() : this.enter();
18945     },
18946     
18947     enter : function () {
18948         
18949         clearTimeout(this.timeout);
18950     
18951         this.hoverState = 'in';
18952     
18953         if (!this.delay || !this.delay.show) {
18954             this.show();
18955             return;
18956         }
18957         var _t = this;
18958         this.timeout = setTimeout(function () {
18959             if (_t.hoverState == 'in') {
18960                 _t.show();
18961             }
18962         }, this.delay.show)
18963     },
18964     
18965     leave : function() {
18966         clearTimeout(this.timeout);
18967     
18968         this.hoverState = 'out';
18969     
18970         if (!this.delay || !this.delay.hide) {
18971             this.hide();
18972             return;
18973         }
18974         var _t = this;
18975         this.timeout = setTimeout(function () {
18976             if (_t.hoverState == 'out') {
18977                 _t.hide();
18978             }
18979         }, this.delay.hide)
18980     },
18981     
18982     show : function (on_el)
18983     {
18984         if (!on_el) {
18985             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18986         }
18987         
18988         // set content.
18989         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18990         if (this.html !== false) {
18991             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18992         }
18993         this.el.removeClass([
18994             'fade','top','bottom', 'left', 'right','in',
18995             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18996         ]);
18997         if (!this.title.length) {
18998             this.el.select('.popover-title',true).hide();
18999         }
19000         
19001         var placement = typeof this.placement == 'function' ?
19002             this.placement.call(this, this.el, on_el) :
19003             this.placement;
19004             
19005         var autoToken = /\s?auto?\s?/i;
19006         var autoPlace = autoToken.test(placement);
19007         if (autoPlace) {
19008             placement = placement.replace(autoToken, '') || 'top';
19009         }
19010         
19011         //this.el.detach()
19012         //this.el.setXY([0,0]);
19013         this.el.show();
19014         this.el.dom.style.display='block';
19015         this.el.addClass(placement);
19016         
19017         //this.el.appendTo(on_el);
19018         
19019         var p = this.getPosition();
19020         var box = this.el.getBox();
19021         
19022         if (autoPlace) {
19023             // fixme..
19024         }
19025         var align = Roo.bootstrap.Popover.alignment[placement];
19026         
19027 //        Roo.log(align);
19028         this.el.alignTo(on_el, align[0],align[1]);
19029         //var arrow = this.el.select('.arrow',true).first();
19030         //arrow.set(align[2], 
19031         
19032         this.el.addClass('in');
19033         
19034         
19035         if (this.el.hasClass('fade')) {
19036             // fade it?
19037         }
19038         
19039         this.hoverState = 'in';
19040         
19041         this.fireEvent('show', this);
19042         
19043     },
19044     hide : function()
19045     {
19046         this.el.setXY([0,0]);
19047         this.el.removeClass('in');
19048         this.el.hide();
19049         this.hoverState = null;
19050         
19051         this.fireEvent('hide', this);
19052     }
19053     
19054 });
19055
19056 Roo.bootstrap.Popover.alignment = {
19057     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19058     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19059     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19060     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19061 };
19062
19063  /*
19064  * - LGPL
19065  *
19066  * Progress
19067  * 
19068  */
19069
19070 /**
19071  * @class Roo.bootstrap.Progress
19072  * @extends Roo.bootstrap.Component
19073  * Bootstrap Progress class
19074  * @cfg {Boolean} striped striped of the progress bar
19075  * @cfg {Boolean} active animated of the progress bar
19076  * 
19077  * 
19078  * @constructor
19079  * Create a new Progress
19080  * @param {Object} config The config object
19081  */
19082
19083 Roo.bootstrap.Progress = function(config){
19084     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19085 };
19086
19087 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19088     
19089     striped : false,
19090     active: false,
19091     
19092     getAutoCreate : function(){
19093         var cfg = {
19094             tag: 'div',
19095             cls: 'progress'
19096         };
19097         
19098         
19099         if(this.striped){
19100             cfg.cls += ' progress-striped';
19101         }
19102       
19103         if(this.active){
19104             cfg.cls += ' active';
19105         }
19106         
19107         
19108         return cfg;
19109     }
19110    
19111 });
19112
19113  
19114
19115  /*
19116  * - LGPL
19117  *
19118  * ProgressBar
19119  * 
19120  */
19121
19122 /**
19123  * @class Roo.bootstrap.ProgressBar
19124  * @extends Roo.bootstrap.Component
19125  * Bootstrap ProgressBar class
19126  * @cfg {Number} aria_valuenow aria-value now
19127  * @cfg {Number} aria_valuemin aria-value min
19128  * @cfg {Number} aria_valuemax aria-value max
19129  * @cfg {String} label label for the progress bar
19130  * @cfg {String} panel (success | info | warning | danger )
19131  * @cfg {String} role role of the progress bar
19132  * @cfg {String} sr_only text
19133  * 
19134  * 
19135  * @constructor
19136  * Create a new ProgressBar
19137  * @param {Object} config The config object
19138  */
19139
19140 Roo.bootstrap.ProgressBar = function(config){
19141     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19142 };
19143
19144 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19145     
19146     aria_valuenow : 0,
19147     aria_valuemin : 0,
19148     aria_valuemax : 100,
19149     label : false,
19150     panel : false,
19151     role : false,
19152     sr_only: false,
19153     
19154     getAutoCreate : function()
19155     {
19156         
19157         var cfg = {
19158             tag: 'div',
19159             cls: 'progress-bar',
19160             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19161         };
19162         
19163         if(this.sr_only){
19164             cfg.cn = {
19165                 tag: 'span',
19166                 cls: 'sr-only',
19167                 html: this.sr_only
19168             }
19169         }
19170         
19171         if(this.role){
19172             cfg.role = this.role;
19173         }
19174         
19175         if(this.aria_valuenow){
19176             cfg['aria-valuenow'] = this.aria_valuenow;
19177         }
19178         
19179         if(this.aria_valuemin){
19180             cfg['aria-valuemin'] = this.aria_valuemin;
19181         }
19182         
19183         if(this.aria_valuemax){
19184             cfg['aria-valuemax'] = this.aria_valuemax;
19185         }
19186         
19187         if(this.label && !this.sr_only){
19188             cfg.html = this.label;
19189         }
19190         
19191         if(this.panel){
19192             cfg.cls += ' progress-bar-' + this.panel;
19193         }
19194         
19195         return cfg;
19196     },
19197     
19198     update : function(aria_valuenow)
19199     {
19200         this.aria_valuenow = aria_valuenow;
19201         
19202         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19203     }
19204    
19205 });
19206
19207  
19208
19209  /*
19210  * - LGPL
19211  *
19212  * column
19213  * 
19214  */
19215
19216 /**
19217  * @class Roo.bootstrap.TabGroup
19218  * @extends Roo.bootstrap.Column
19219  * Bootstrap Column class
19220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19221  * @cfg {Boolean} carousel true to make the group behave like a carousel
19222  * @cfg {Boolean} bullets show bullets for the panels
19223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19225  * @cfg {Boolean} showarrow (true|false) show arrow default true
19226  * 
19227  * @constructor
19228  * Create a new TabGroup
19229  * @param {Object} config The config object
19230  */
19231
19232 Roo.bootstrap.TabGroup = function(config){
19233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19234     if (!this.navId) {
19235         this.navId = Roo.id();
19236     }
19237     this.tabs = [];
19238     Roo.bootstrap.TabGroup.register(this);
19239     
19240 };
19241
19242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19243     
19244     carousel : false,
19245     transition : false,
19246     bullets : 0,
19247     timer : 0,
19248     autoslide : false,
19249     slideFn : false,
19250     slideOnTouch : false,
19251     showarrow : true,
19252     
19253     getAutoCreate : function()
19254     {
19255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19256         
19257         cfg.cls += ' tab-content';
19258         
19259         if (this.carousel) {
19260             cfg.cls += ' carousel slide';
19261             
19262             cfg.cn = [{
19263                cls : 'carousel-inner',
19264                cn : []
19265             }];
19266         
19267             if(this.bullets  && !Roo.isTouch){
19268                 
19269                 var bullets = {
19270                     cls : 'carousel-bullets',
19271                     cn : []
19272                 };
19273                
19274                 if(this.bullets_cls){
19275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19276                 }
19277                 
19278                 bullets.cn.push({
19279                     cls : 'clear'
19280                 });
19281                 
19282                 cfg.cn[0].cn.push(bullets);
19283             }
19284             
19285             if(this.showarrow){
19286                 cfg.cn[0].cn.push({
19287                     tag : 'div',
19288                     class : 'carousel-arrow',
19289                     cn : [
19290                         {
19291                             tag : 'div',
19292                             class : 'carousel-prev',
19293                             cn : [
19294                                 {
19295                                     tag : 'i',
19296                                     class : 'fa fa-chevron-left'
19297                                 }
19298                             ]
19299                         },
19300                         {
19301                             tag : 'div',
19302                             class : 'carousel-next',
19303                             cn : [
19304                                 {
19305                                     tag : 'i',
19306                                     class : 'fa fa-chevron-right'
19307                                 }
19308                             ]
19309                         }
19310                     ]
19311                 });
19312             }
19313             
19314         }
19315         
19316         return cfg;
19317     },
19318     
19319     initEvents:  function()
19320     {
19321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19322 //            this.el.on("touchstart", this.onTouchStart, this);
19323 //        }
19324         
19325         if(this.autoslide){
19326             var _this = this;
19327             
19328             this.slideFn = window.setInterval(function() {
19329                 _this.showPanelNext();
19330             }, this.timer);
19331         }
19332         
19333         if(this.showarrow){
19334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19336         }
19337         
19338         
19339     },
19340     
19341 //    onTouchStart : function(e, el, o)
19342 //    {
19343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19344 //            return;
19345 //        }
19346 //        
19347 //        this.showPanelNext();
19348 //    },
19349     
19350     
19351     getChildContainer : function()
19352     {
19353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19354     },
19355     
19356     /**
19357     * register a Navigation item
19358     * @param {Roo.bootstrap.NavItem} the navitem to add
19359     */
19360     register : function(item)
19361     {
19362         this.tabs.push( item);
19363         item.navId = this.navId; // not really needed..
19364         this.addBullet();
19365     
19366     },
19367     
19368     getActivePanel : function()
19369     {
19370         var r = false;
19371         Roo.each(this.tabs, function(t) {
19372             if (t.active) {
19373                 r = t;
19374                 return false;
19375             }
19376             return null;
19377         });
19378         return r;
19379         
19380     },
19381     getPanelByName : function(n)
19382     {
19383         var r = false;
19384         Roo.each(this.tabs, function(t) {
19385             if (t.tabId == n) {
19386                 r = t;
19387                 return false;
19388             }
19389             return null;
19390         });
19391         return r;
19392     },
19393     indexOfPanel : function(p)
19394     {
19395         var r = false;
19396         Roo.each(this.tabs, function(t,i) {
19397             if (t.tabId == p.tabId) {
19398                 r = i;
19399                 return false;
19400             }
19401             return null;
19402         });
19403         return r;
19404     },
19405     /**
19406      * show a specific panel
19407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19409      */
19410     showPanel : function (pan)
19411     {
19412         if(this.transition || typeof(pan) == 'undefined'){
19413             Roo.log("waiting for the transitionend");
19414             return false;
19415         }
19416         
19417         if (typeof(pan) == 'number') {
19418             pan = this.tabs[pan];
19419         }
19420         
19421         if (typeof(pan) == 'string') {
19422             pan = this.getPanelByName(pan);
19423         }
19424         
19425         var cur = this.getActivePanel();
19426         
19427         if(!pan || !cur){
19428             Roo.log('pan or acitve pan is undefined');
19429             return false;
19430         }
19431         
19432         if (pan.tabId == this.getActivePanel().tabId) {
19433             return true;
19434         }
19435         
19436         if (false === cur.fireEvent('beforedeactivate')) {
19437             return false;
19438         }
19439         
19440         if(this.bullets > 0 && !Roo.isTouch){
19441             this.setActiveBullet(this.indexOfPanel(pan));
19442         }
19443         
19444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19445             
19446             //class="carousel-item carousel-item-next carousel-item-left"
19447             
19448             this.transition = true;
19449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19450             var lr = dir == 'next' ? 'left' : 'right';
19451             pan.el.addClass(dir); // or prev
19452             pan.el.addClass('carousel-item-' + dir); // or prev
19453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19454             cur.el.addClass(lr); // or right
19455             pan.el.addClass(lr);
19456             cur.el.addClass('carousel-item-' +lr); // or right
19457             pan.el.addClass('carousel-item-' +lr);
19458             
19459             
19460             var _this = this;
19461             cur.el.on('transitionend', function() {
19462                 Roo.log("trans end?");
19463                 
19464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19465                 pan.setActive(true);
19466                 
19467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19468                 cur.setActive(false);
19469                 
19470                 _this.transition = false;
19471                 
19472             }, this, { single:  true } );
19473             
19474             return true;
19475         }
19476         
19477         cur.setActive(false);
19478         pan.setActive(true);
19479         
19480         return true;
19481         
19482     },
19483     showPanelNext : function()
19484     {
19485         var i = this.indexOfPanel(this.getActivePanel());
19486         
19487         if (i >= this.tabs.length - 1 && !this.autoslide) {
19488             return;
19489         }
19490         
19491         if (i >= this.tabs.length - 1 && this.autoslide) {
19492             i = -1;
19493         }
19494         
19495         this.showPanel(this.tabs[i+1]);
19496     },
19497     
19498     showPanelPrev : function()
19499     {
19500         var i = this.indexOfPanel(this.getActivePanel());
19501         
19502         if (i  < 1 && !this.autoslide) {
19503             return;
19504         }
19505         
19506         if (i < 1 && this.autoslide) {
19507             i = this.tabs.length;
19508         }
19509         
19510         this.showPanel(this.tabs[i-1]);
19511     },
19512     
19513     
19514     addBullet: function()
19515     {
19516         if(!this.bullets || Roo.isTouch){
19517             return;
19518         }
19519         var ctr = this.el.select('.carousel-bullets',true).first();
19520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19521         var bullet = ctr.createChild({
19522             cls : 'bullet bullet-' + i
19523         },ctr.dom.lastChild);
19524         
19525         
19526         var _this = this;
19527         
19528         bullet.on('click', (function(e, el, o, ii, t){
19529
19530             e.preventDefault();
19531
19532             this.showPanel(ii);
19533
19534             if(this.autoslide && this.slideFn){
19535                 clearInterval(this.slideFn);
19536                 this.slideFn = window.setInterval(function() {
19537                     _this.showPanelNext();
19538                 }, this.timer);
19539             }
19540
19541         }).createDelegate(this, [i, bullet], true));
19542                 
19543         
19544     },
19545      
19546     setActiveBullet : function(i)
19547     {
19548         if(Roo.isTouch){
19549             return;
19550         }
19551         
19552         Roo.each(this.el.select('.bullet', true).elements, function(el){
19553             el.removeClass('selected');
19554         });
19555
19556         var bullet = this.el.select('.bullet-' + i, true).first();
19557         
19558         if(!bullet){
19559             return;
19560         }
19561         
19562         bullet.addClass('selected');
19563     }
19564     
19565     
19566   
19567 });
19568
19569  
19570
19571  
19572  
19573 Roo.apply(Roo.bootstrap.TabGroup, {
19574     
19575     groups: {},
19576      /**
19577     * register a Navigation Group
19578     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19579     */
19580     register : function(navgrp)
19581     {
19582         this.groups[navgrp.navId] = navgrp;
19583         
19584     },
19585     /**
19586     * fetch a Navigation Group based on the navigation ID
19587     * if one does not exist , it will get created.
19588     * @param {string} the navgroup to add
19589     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19590     */
19591     get: function(navId) {
19592         if (typeof(this.groups[navId]) == 'undefined') {
19593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19594         }
19595         return this.groups[navId] ;
19596     }
19597     
19598     
19599     
19600 });
19601
19602  /*
19603  * - LGPL
19604  *
19605  * TabPanel
19606  * 
19607  */
19608
19609 /**
19610  * @class Roo.bootstrap.TabPanel
19611  * @extends Roo.bootstrap.Component
19612  * Bootstrap TabPanel class
19613  * @cfg {Boolean} active panel active
19614  * @cfg {String} html panel content
19615  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19616  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19617  * @cfg {String} href click to link..
19618  * 
19619  * 
19620  * @constructor
19621  * Create a new TabPanel
19622  * @param {Object} config The config object
19623  */
19624
19625 Roo.bootstrap.TabPanel = function(config){
19626     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19627     this.addEvents({
19628         /**
19629              * @event changed
19630              * Fires when the active status changes
19631              * @param {Roo.bootstrap.TabPanel} this
19632              * @param {Boolean} state the new state
19633             
19634          */
19635         'changed': true,
19636         /**
19637              * @event beforedeactivate
19638              * Fires before a tab is de-activated - can be used to do validation on a form.
19639              * @param {Roo.bootstrap.TabPanel} this
19640              * @return {Boolean} false if there is an error
19641             
19642          */
19643         'beforedeactivate': true
19644      });
19645     
19646     this.tabId = this.tabId || Roo.id();
19647   
19648 };
19649
19650 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19651     
19652     active: false,
19653     html: false,
19654     tabId: false,
19655     navId : false,
19656     href : '',
19657     
19658     getAutoCreate : function(){
19659         
19660         
19661         var cfg = {
19662             tag: 'div',
19663             // item is needed for carousel - not sure if it has any effect otherwise
19664             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19665             html: this.html || ''
19666         };
19667         
19668         if(this.active){
19669             cfg.cls += ' active';
19670         }
19671         
19672         if(this.tabId){
19673             cfg.tabId = this.tabId;
19674         }
19675         
19676         
19677         
19678         return cfg;
19679     },
19680     
19681     initEvents:  function()
19682     {
19683         var p = this.parent();
19684         
19685         this.navId = this.navId || p.navId;
19686         
19687         if (typeof(this.navId) != 'undefined') {
19688             // not really needed.. but just in case.. parent should be a NavGroup.
19689             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19690             
19691             tg.register(this);
19692             
19693             var i = tg.tabs.length - 1;
19694             
19695             if(this.active && tg.bullets > 0 && i < tg.bullets){
19696                 tg.setActiveBullet(i);
19697             }
19698         }
19699         
19700         this.el.on('click', this.onClick, this);
19701         
19702         if(Roo.isTouch){
19703             this.el.on("touchstart", this.onTouchStart, this);
19704             this.el.on("touchmove", this.onTouchMove, this);
19705             this.el.on("touchend", this.onTouchEnd, this);
19706         }
19707         
19708     },
19709     
19710     onRender : function(ct, position)
19711     {
19712         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19713     },
19714     
19715     setActive : function(state)
19716     {
19717         Roo.log("panel - set active " + this.tabId + "=" + state);
19718         
19719         this.active = state;
19720         if (!state) {
19721             this.el.removeClass('active');
19722             
19723         } else  if (!this.el.hasClass('active')) {
19724             this.el.addClass('active');
19725         }
19726         
19727         this.fireEvent('changed', this, state);
19728     },
19729     
19730     onClick : function(e)
19731     {
19732         e.preventDefault();
19733         
19734         if(!this.href.length){
19735             return;
19736         }
19737         
19738         window.location.href = this.href;
19739     },
19740     
19741     startX : 0,
19742     startY : 0,
19743     endX : 0,
19744     endY : 0,
19745     swiping : false,
19746     
19747     onTouchStart : function(e)
19748     {
19749         this.swiping = false;
19750         
19751         this.startX = e.browserEvent.touches[0].clientX;
19752         this.startY = e.browserEvent.touches[0].clientY;
19753     },
19754     
19755     onTouchMove : function(e)
19756     {
19757         this.swiping = true;
19758         
19759         this.endX = e.browserEvent.touches[0].clientX;
19760         this.endY = e.browserEvent.touches[0].clientY;
19761     },
19762     
19763     onTouchEnd : function(e)
19764     {
19765         if(!this.swiping){
19766             this.onClick(e);
19767             return;
19768         }
19769         
19770         var tabGroup = this.parent();
19771         
19772         if(this.endX > this.startX){ // swiping right
19773             tabGroup.showPanelPrev();
19774             return;
19775         }
19776         
19777         if(this.startX > this.endX){ // swiping left
19778             tabGroup.showPanelNext();
19779             return;
19780         }
19781     }
19782     
19783     
19784 });
19785  
19786
19787  
19788
19789  /*
19790  * - LGPL
19791  *
19792  * DateField
19793  * 
19794  */
19795
19796 /**
19797  * @class Roo.bootstrap.DateField
19798  * @extends Roo.bootstrap.Input
19799  * Bootstrap DateField class
19800  * @cfg {Number} weekStart default 0
19801  * @cfg {String} viewMode default empty, (months|years)
19802  * @cfg {String} minViewMode default empty, (months|years)
19803  * @cfg {Number} startDate default -Infinity
19804  * @cfg {Number} endDate default Infinity
19805  * @cfg {Boolean} todayHighlight default false
19806  * @cfg {Boolean} todayBtn default false
19807  * @cfg {Boolean} calendarWeeks default false
19808  * @cfg {Object} daysOfWeekDisabled default empty
19809  * @cfg {Boolean} singleMode default false (true | false)
19810  * 
19811  * @cfg {Boolean} keyboardNavigation default true
19812  * @cfg {String} language default en
19813  * 
19814  * @constructor
19815  * Create a new DateField
19816  * @param {Object} config The config object
19817  */
19818
19819 Roo.bootstrap.DateField = function(config){
19820     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19821      this.addEvents({
19822             /**
19823              * @event show
19824              * Fires when this field show.
19825              * @param {Roo.bootstrap.DateField} this
19826              * @param {Mixed} date The date value
19827              */
19828             show : true,
19829             /**
19830              * @event show
19831              * Fires when this field hide.
19832              * @param {Roo.bootstrap.DateField} this
19833              * @param {Mixed} date The date value
19834              */
19835             hide : true,
19836             /**
19837              * @event select
19838              * Fires when select a date.
19839              * @param {Roo.bootstrap.DateField} this
19840              * @param {Mixed} date The date value
19841              */
19842             select : true,
19843             /**
19844              * @event beforeselect
19845              * Fires when before select a date.
19846              * @param {Roo.bootstrap.DateField} this
19847              * @param {Mixed} date The date value
19848              */
19849             beforeselect : true
19850         });
19851 };
19852
19853 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19854     
19855     /**
19856      * @cfg {String} format
19857      * The default date format string which can be overriden for localization support.  The format must be
19858      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19859      */
19860     format : "m/d/y",
19861     /**
19862      * @cfg {String} altFormats
19863      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19864      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19865      */
19866     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19867     
19868     weekStart : 0,
19869     
19870     viewMode : '',
19871     
19872     minViewMode : '',
19873     
19874     todayHighlight : false,
19875     
19876     todayBtn: false,
19877     
19878     language: 'en',
19879     
19880     keyboardNavigation: true,
19881     
19882     calendarWeeks: false,
19883     
19884     startDate: -Infinity,
19885     
19886     endDate: Infinity,
19887     
19888     daysOfWeekDisabled: [],
19889     
19890     _events: [],
19891     
19892     singleMode : false,
19893     
19894     UTCDate: function()
19895     {
19896         return new Date(Date.UTC.apply(Date, arguments));
19897     },
19898     
19899     UTCToday: function()
19900     {
19901         var today = new Date();
19902         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19903     },
19904     
19905     getDate: function() {
19906             var d = this.getUTCDate();
19907             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19908     },
19909     
19910     getUTCDate: function() {
19911             return this.date;
19912     },
19913     
19914     setDate: function(d) {
19915             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19916     },
19917     
19918     setUTCDate: function(d) {
19919             this.date = d;
19920             this.setValue(this.formatDate(this.date));
19921     },
19922         
19923     onRender: function(ct, position)
19924     {
19925         
19926         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19927         
19928         this.language = this.language || 'en';
19929         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19930         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19931         
19932         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19933         this.format = this.format || 'm/d/y';
19934         this.isInline = false;
19935         this.isInput = true;
19936         this.component = this.el.select('.add-on', true).first() || false;
19937         this.component = (this.component && this.component.length === 0) ? false : this.component;
19938         this.hasInput = this.component && this.inputEl().length;
19939         
19940         if (typeof(this.minViewMode === 'string')) {
19941             switch (this.minViewMode) {
19942                 case 'months':
19943                     this.minViewMode = 1;
19944                     break;
19945                 case 'years':
19946                     this.minViewMode = 2;
19947                     break;
19948                 default:
19949                     this.minViewMode = 0;
19950                     break;
19951             }
19952         }
19953         
19954         if (typeof(this.viewMode === 'string')) {
19955             switch (this.viewMode) {
19956                 case 'months':
19957                     this.viewMode = 1;
19958                     break;
19959                 case 'years':
19960                     this.viewMode = 2;
19961                     break;
19962                 default:
19963                     this.viewMode = 0;
19964                     break;
19965             }
19966         }
19967                 
19968         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19969         
19970 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19971         
19972         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19973         
19974         this.picker().on('mousedown', this.onMousedown, this);
19975         this.picker().on('click', this.onClick, this);
19976         
19977         this.picker().addClass('datepicker-dropdown');
19978         
19979         this.startViewMode = this.viewMode;
19980         
19981         if(this.singleMode){
19982             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19983                 v.setVisibilityMode(Roo.Element.DISPLAY);
19984                 v.hide();
19985             });
19986             
19987             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19988                 v.setStyle('width', '189px');
19989             });
19990         }
19991         
19992         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19993             if(!this.calendarWeeks){
19994                 v.remove();
19995                 return;
19996             }
19997             
19998             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19999             v.attr('colspan', function(i, val){
20000                 return parseInt(val) + 1;
20001             });
20002         });
20003                         
20004         
20005         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20006         
20007         this.setStartDate(this.startDate);
20008         this.setEndDate(this.endDate);
20009         
20010         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20011         
20012         this.fillDow();
20013         this.fillMonths();
20014         this.update();
20015         this.showMode();
20016         
20017         if(this.isInline) {
20018             this.showPopup();
20019         }
20020     },
20021     
20022     picker : function()
20023     {
20024         return this.pickerEl;
20025 //        return this.el.select('.datepicker', true).first();
20026     },
20027     
20028     fillDow: function()
20029     {
20030         var dowCnt = this.weekStart;
20031         
20032         var dow = {
20033             tag: 'tr',
20034             cn: [
20035                 
20036             ]
20037         };
20038         
20039         if(this.calendarWeeks){
20040             dow.cn.push({
20041                 tag: 'th',
20042                 cls: 'cw',
20043                 html: '&nbsp;'
20044             })
20045         }
20046         
20047         while (dowCnt < this.weekStart + 7) {
20048             dow.cn.push({
20049                 tag: 'th',
20050                 cls: 'dow',
20051                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20052             });
20053         }
20054         
20055         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20056     },
20057     
20058     fillMonths: function()
20059     {    
20060         var i = 0;
20061         var months = this.picker().select('>.datepicker-months td', true).first();
20062         
20063         months.dom.innerHTML = '';
20064         
20065         while (i < 12) {
20066             var month = {
20067                 tag: 'span',
20068                 cls: 'month',
20069                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20070             };
20071             
20072             months.createChild(month);
20073         }
20074         
20075     },
20076     
20077     update: function()
20078     {
20079         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;
20080         
20081         if (this.date < this.startDate) {
20082             this.viewDate = new Date(this.startDate);
20083         } else if (this.date > this.endDate) {
20084             this.viewDate = new Date(this.endDate);
20085         } else {
20086             this.viewDate = new Date(this.date);
20087         }
20088         
20089         this.fill();
20090     },
20091     
20092     fill: function() 
20093     {
20094         var d = new Date(this.viewDate),
20095                 year = d.getUTCFullYear(),
20096                 month = d.getUTCMonth(),
20097                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20098                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20099                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20100                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20101                 currentDate = this.date && this.date.valueOf(),
20102                 today = this.UTCToday();
20103         
20104         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20105         
20106 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20107         
20108 //        this.picker.select('>tfoot th.today').
20109 //                                              .text(dates[this.language].today)
20110 //                                              .toggle(this.todayBtn !== false);
20111     
20112         this.updateNavArrows();
20113         this.fillMonths();
20114                                                 
20115         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20116         
20117         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20118          
20119         prevMonth.setUTCDate(day);
20120         
20121         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20122         
20123         var nextMonth = new Date(prevMonth);
20124         
20125         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20126         
20127         nextMonth = nextMonth.valueOf();
20128         
20129         var fillMonths = false;
20130         
20131         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20132         
20133         while(prevMonth.valueOf() <= nextMonth) {
20134             var clsName = '';
20135             
20136             if (prevMonth.getUTCDay() === this.weekStart) {
20137                 if(fillMonths){
20138                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20139                 }
20140                     
20141                 fillMonths = {
20142                     tag: 'tr',
20143                     cn: []
20144                 };
20145                 
20146                 if(this.calendarWeeks){
20147                     // ISO 8601: First week contains first thursday.
20148                     // ISO also states week starts on Monday, but we can be more abstract here.
20149                     var
20150                     // Start of current week: based on weekstart/current date
20151                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20152                     // Thursday of this week
20153                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20154                     // First Thursday of year, year from thursday
20155                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20156                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20157                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20158                     
20159                     fillMonths.cn.push({
20160                         tag: 'td',
20161                         cls: 'cw',
20162                         html: calWeek
20163                     });
20164                 }
20165             }
20166             
20167             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20168                 clsName += ' old';
20169             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20170                 clsName += ' new';
20171             }
20172             if (this.todayHighlight &&
20173                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20174                 prevMonth.getUTCMonth() == today.getMonth() &&
20175                 prevMonth.getUTCDate() == today.getDate()) {
20176                 clsName += ' today';
20177             }
20178             
20179             if (currentDate && prevMonth.valueOf() === currentDate) {
20180                 clsName += ' active';
20181             }
20182             
20183             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20184                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20185                     clsName += ' disabled';
20186             }
20187             
20188             fillMonths.cn.push({
20189                 tag: 'td',
20190                 cls: 'day ' + clsName,
20191                 html: prevMonth.getDate()
20192             });
20193             
20194             prevMonth.setDate(prevMonth.getDate()+1);
20195         }
20196           
20197         var currentYear = this.date && this.date.getUTCFullYear();
20198         var currentMonth = this.date && this.date.getUTCMonth();
20199         
20200         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20201         
20202         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20203             v.removeClass('active');
20204             
20205             if(currentYear === year && k === currentMonth){
20206                 v.addClass('active');
20207             }
20208             
20209             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20210                 v.addClass('disabled');
20211             }
20212             
20213         });
20214         
20215         
20216         year = parseInt(year/10, 10) * 10;
20217         
20218         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20219         
20220         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20221         
20222         year -= 1;
20223         for (var i = -1; i < 11; i++) {
20224             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20225                 tag: 'span',
20226                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20227                 html: year
20228             });
20229             
20230             year += 1;
20231         }
20232     },
20233     
20234     showMode: function(dir) 
20235     {
20236         if (dir) {
20237             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20238         }
20239         
20240         Roo.each(this.picker().select('>div',true).elements, function(v){
20241             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20242             v.hide();
20243         });
20244         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20245     },
20246     
20247     place: function()
20248     {
20249         if(this.isInline) {
20250             return;
20251         }
20252         
20253         this.picker().removeClass(['bottom', 'top']);
20254         
20255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20256             /*
20257              * place to the top of element!
20258              *
20259              */
20260             
20261             this.picker().addClass('top');
20262             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20263             
20264             return;
20265         }
20266         
20267         this.picker().addClass('bottom');
20268         
20269         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20270     },
20271     
20272     parseDate : function(value)
20273     {
20274         if(!value || value instanceof Date){
20275             return value;
20276         }
20277         var v = Date.parseDate(value, this.format);
20278         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20279             v = Date.parseDate(value, 'Y-m-d');
20280         }
20281         if(!v && this.altFormats){
20282             if(!this.altFormatsArray){
20283                 this.altFormatsArray = this.altFormats.split("|");
20284             }
20285             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20286                 v = Date.parseDate(value, this.altFormatsArray[i]);
20287             }
20288         }
20289         return v;
20290     },
20291     
20292     formatDate : function(date, fmt)
20293     {   
20294         return (!date || !(date instanceof Date)) ?
20295         date : date.dateFormat(fmt || this.format);
20296     },
20297     
20298     onFocus : function()
20299     {
20300         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20301         this.showPopup();
20302     },
20303     
20304     onBlur : function()
20305     {
20306         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20307         
20308         var d = this.inputEl().getValue();
20309         
20310         this.setValue(d);
20311                 
20312         this.hidePopup();
20313     },
20314     
20315     showPopup : function()
20316     {
20317         this.picker().show();
20318         this.update();
20319         this.place();
20320         
20321         this.fireEvent('showpopup', this, this.date);
20322     },
20323     
20324     hidePopup : function()
20325     {
20326         if(this.isInline) {
20327             return;
20328         }
20329         this.picker().hide();
20330         this.viewMode = this.startViewMode;
20331         this.showMode();
20332         
20333         this.fireEvent('hidepopup', this, this.date);
20334         
20335     },
20336     
20337     onMousedown: function(e)
20338     {
20339         e.stopPropagation();
20340         e.preventDefault();
20341     },
20342     
20343     keyup: function(e)
20344     {
20345         Roo.bootstrap.DateField.superclass.keyup.call(this);
20346         this.update();
20347     },
20348
20349     setValue: function(v)
20350     {
20351         if(this.fireEvent('beforeselect', this, v) !== false){
20352             var d = new Date(this.parseDate(v) ).clearTime();
20353         
20354             if(isNaN(d.getTime())){
20355                 this.date = this.viewDate = '';
20356                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20357                 return;
20358             }
20359
20360             v = this.formatDate(d);
20361
20362             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20363
20364             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20365
20366             this.update();
20367
20368             this.fireEvent('select', this, this.date);
20369         }
20370     },
20371     
20372     getValue: function()
20373     {
20374         return this.formatDate(this.date);
20375     },
20376     
20377     fireKey: function(e)
20378     {
20379         if (!this.picker().isVisible()){
20380             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20381                 this.showPopup();
20382             }
20383             return;
20384         }
20385         
20386         var dateChanged = false,
20387         dir, day, month,
20388         newDate, newViewDate;
20389         
20390         switch(e.keyCode){
20391             case 27: // escape
20392                 this.hidePopup();
20393                 e.preventDefault();
20394                 break;
20395             case 37: // left
20396             case 39: // right
20397                 if (!this.keyboardNavigation) {
20398                     break;
20399                 }
20400                 dir = e.keyCode == 37 ? -1 : 1;
20401                 
20402                 if (e.ctrlKey){
20403                     newDate = this.moveYear(this.date, dir);
20404                     newViewDate = this.moveYear(this.viewDate, dir);
20405                 } else if (e.shiftKey){
20406                     newDate = this.moveMonth(this.date, dir);
20407                     newViewDate = this.moveMonth(this.viewDate, dir);
20408                 } else {
20409                     newDate = new Date(this.date);
20410                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20411                     newViewDate = new Date(this.viewDate);
20412                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20413                 }
20414                 if (this.dateWithinRange(newDate)){
20415                     this.date = newDate;
20416                     this.viewDate = newViewDate;
20417                     this.setValue(this.formatDate(this.date));
20418 //                    this.update();
20419                     e.preventDefault();
20420                     dateChanged = true;
20421                 }
20422                 break;
20423             case 38: // up
20424             case 40: // down
20425                 if (!this.keyboardNavigation) {
20426                     break;
20427                 }
20428                 dir = e.keyCode == 38 ? -1 : 1;
20429                 if (e.ctrlKey){
20430                     newDate = this.moveYear(this.date, dir);
20431                     newViewDate = this.moveYear(this.viewDate, dir);
20432                 } else if (e.shiftKey){
20433                     newDate = this.moveMonth(this.date, dir);
20434                     newViewDate = this.moveMonth(this.viewDate, dir);
20435                 } else {
20436                     newDate = new Date(this.date);
20437                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20438                     newViewDate = new Date(this.viewDate);
20439                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20440                 }
20441                 if (this.dateWithinRange(newDate)){
20442                     this.date = newDate;
20443                     this.viewDate = newViewDate;
20444                     this.setValue(this.formatDate(this.date));
20445 //                    this.update();
20446                     e.preventDefault();
20447                     dateChanged = true;
20448                 }
20449                 break;
20450             case 13: // enter
20451                 this.setValue(this.formatDate(this.date));
20452                 this.hidePopup();
20453                 e.preventDefault();
20454                 break;
20455             case 9: // tab
20456                 this.setValue(this.formatDate(this.date));
20457                 this.hidePopup();
20458                 break;
20459             case 16: // shift
20460             case 17: // ctrl
20461             case 18: // alt
20462                 break;
20463             default :
20464                 this.hidePopup();
20465                 
20466         }
20467     },
20468     
20469     
20470     onClick: function(e) 
20471     {
20472         e.stopPropagation();
20473         e.preventDefault();
20474         
20475         var target = e.getTarget();
20476         
20477         if(target.nodeName.toLowerCase() === 'i'){
20478             target = Roo.get(target).dom.parentNode;
20479         }
20480         
20481         var nodeName = target.nodeName;
20482         var className = target.className;
20483         var html = target.innerHTML;
20484         //Roo.log(nodeName);
20485         
20486         switch(nodeName.toLowerCase()) {
20487             case 'th':
20488                 switch(className) {
20489                     case 'switch':
20490                         this.showMode(1);
20491                         break;
20492                     case 'prev':
20493                     case 'next':
20494                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20495                         switch(this.viewMode){
20496                                 case 0:
20497                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20498                                         break;
20499                                 case 1:
20500                                 case 2:
20501                                         this.viewDate = this.moveYear(this.viewDate, dir);
20502                                         break;
20503                         }
20504                         this.fill();
20505                         break;
20506                     case 'today':
20507                         var date = new Date();
20508                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20509 //                        this.fill()
20510                         this.setValue(this.formatDate(this.date));
20511                         
20512                         this.hidePopup();
20513                         break;
20514                 }
20515                 break;
20516             case 'span':
20517                 if (className.indexOf('disabled') < 0) {
20518                     this.viewDate.setUTCDate(1);
20519                     if (className.indexOf('month') > -1) {
20520                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20521                     } else {
20522                         var year = parseInt(html, 10) || 0;
20523                         this.viewDate.setUTCFullYear(year);
20524                         
20525                     }
20526                     
20527                     if(this.singleMode){
20528                         this.setValue(this.formatDate(this.viewDate));
20529                         this.hidePopup();
20530                         return;
20531                     }
20532                     
20533                     this.showMode(-1);
20534                     this.fill();
20535                 }
20536                 break;
20537                 
20538             case 'td':
20539                 //Roo.log(className);
20540                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20541                     var day = parseInt(html, 10) || 1;
20542                     var year = this.viewDate.getUTCFullYear(),
20543                         month = this.viewDate.getUTCMonth();
20544
20545                     if (className.indexOf('old') > -1) {
20546                         if(month === 0 ){
20547                             month = 11;
20548                             year -= 1;
20549                         }else{
20550                             month -= 1;
20551                         }
20552                     } else if (className.indexOf('new') > -1) {
20553                         if (month == 11) {
20554                             month = 0;
20555                             year += 1;
20556                         } else {
20557                             month += 1;
20558                         }
20559                     }
20560                     //Roo.log([year,month,day]);
20561                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20562                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20563 //                    this.fill();
20564                     //Roo.log(this.formatDate(this.date));
20565                     this.setValue(this.formatDate(this.date));
20566                     this.hidePopup();
20567                 }
20568                 break;
20569         }
20570     },
20571     
20572     setStartDate: function(startDate)
20573     {
20574         this.startDate = startDate || -Infinity;
20575         if (this.startDate !== -Infinity) {
20576             this.startDate = this.parseDate(this.startDate);
20577         }
20578         this.update();
20579         this.updateNavArrows();
20580     },
20581
20582     setEndDate: function(endDate)
20583     {
20584         this.endDate = endDate || Infinity;
20585         if (this.endDate !== Infinity) {
20586             this.endDate = this.parseDate(this.endDate);
20587         }
20588         this.update();
20589         this.updateNavArrows();
20590     },
20591     
20592     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20593     {
20594         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20595         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20596             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20597         }
20598         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20599             return parseInt(d, 10);
20600         });
20601         this.update();
20602         this.updateNavArrows();
20603     },
20604     
20605     updateNavArrows: function() 
20606     {
20607         if(this.singleMode){
20608             return;
20609         }
20610         
20611         var d = new Date(this.viewDate),
20612         year = d.getUTCFullYear(),
20613         month = d.getUTCMonth();
20614         
20615         Roo.each(this.picker().select('.prev', true).elements, function(v){
20616             v.show();
20617             switch (this.viewMode) {
20618                 case 0:
20619
20620                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20621                         v.hide();
20622                     }
20623                     break;
20624                 case 1:
20625                 case 2:
20626                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20627                         v.hide();
20628                     }
20629                     break;
20630             }
20631         });
20632         
20633         Roo.each(this.picker().select('.next', true).elements, function(v){
20634             v.show();
20635             switch (this.viewMode) {
20636                 case 0:
20637
20638                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20639                         v.hide();
20640                     }
20641                     break;
20642                 case 1:
20643                 case 2:
20644                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20645                         v.hide();
20646                     }
20647                     break;
20648             }
20649         })
20650     },
20651     
20652     moveMonth: function(date, dir)
20653     {
20654         if (!dir) {
20655             return date;
20656         }
20657         var new_date = new Date(date.valueOf()),
20658         day = new_date.getUTCDate(),
20659         month = new_date.getUTCMonth(),
20660         mag = Math.abs(dir),
20661         new_month, test;
20662         dir = dir > 0 ? 1 : -1;
20663         if (mag == 1){
20664             test = dir == -1
20665             // If going back one month, make sure month is not current month
20666             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20667             ? function(){
20668                 return new_date.getUTCMonth() == month;
20669             }
20670             // If going forward one month, make sure month is as expected
20671             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20672             : function(){
20673                 return new_date.getUTCMonth() != new_month;
20674             };
20675             new_month = month + dir;
20676             new_date.setUTCMonth(new_month);
20677             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20678             if (new_month < 0 || new_month > 11) {
20679                 new_month = (new_month + 12) % 12;
20680             }
20681         } else {
20682             // For magnitudes >1, move one month at a time...
20683             for (var i=0; i<mag; i++) {
20684                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20685                 new_date = this.moveMonth(new_date, dir);
20686             }
20687             // ...then reset the day, keeping it in the new month
20688             new_month = new_date.getUTCMonth();
20689             new_date.setUTCDate(day);
20690             test = function(){
20691                 return new_month != new_date.getUTCMonth();
20692             };
20693         }
20694         // Common date-resetting loop -- if date is beyond end of month, make it
20695         // end of month
20696         while (test()){
20697             new_date.setUTCDate(--day);
20698             new_date.setUTCMonth(new_month);
20699         }
20700         return new_date;
20701     },
20702
20703     moveYear: function(date, dir)
20704     {
20705         return this.moveMonth(date, dir*12);
20706     },
20707
20708     dateWithinRange: function(date)
20709     {
20710         return date >= this.startDate && date <= this.endDate;
20711     },
20712
20713     
20714     remove: function() 
20715     {
20716         this.picker().remove();
20717     },
20718     
20719     validateValue : function(value)
20720     {
20721         if(this.getVisibilityEl().hasClass('hidden')){
20722             return true;
20723         }
20724         
20725         if(value.length < 1)  {
20726             if(this.allowBlank){
20727                 return true;
20728             }
20729             return false;
20730         }
20731         
20732         if(value.length < this.minLength){
20733             return false;
20734         }
20735         if(value.length > this.maxLength){
20736             return false;
20737         }
20738         if(this.vtype){
20739             var vt = Roo.form.VTypes;
20740             if(!vt[this.vtype](value, this)){
20741                 return false;
20742             }
20743         }
20744         if(typeof this.validator == "function"){
20745             var msg = this.validator(value);
20746             if(msg !== true){
20747                 return false;
20748             }
20749         }
20750         
20751         if(this.regex && !this.regex.test(value)){
20752             return false;
20753         }
20754         
20755         if(typeof(this.parseDate(value)) == 'undefined'){
20756             return false;
20757         }
20758         
20759         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20760             return false;
20761         }      
20762         
20763         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20764             return false;
20765         } 
20766         
20767         
20768         return true;
20769     },
20770     
20771     reset : function()
20772     {
20773         this.date = this.viewDate = '';
20774         
20775         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20776     }
20777    
20778 });
20779
20780 Roo.apply(Roo.bootstrap.DateField,  {
20781     
20782     head : {
20783         tag: 'thead',
20784         cn: [
20785         {
20786             tag: 'tr',
20787             cn: [
20788             {
20789                 tag: 'th',
20790                 cls: 'prev',
20791                 html: '<i class="fa fa-arrow-left"/>'
20792             },
20793             {
20794                 tag: 'th',
20795                 cls: 'switch',
20796                 colspan: '5'
20797             },
20798             {
20799                 tag: 'th',
20800                 cls: 'next',
20801                 html: '<i class="fa fa-arrow-right"/>'
20802             }
20803
20804             ]
20805         }
20806         ]
20807     },
20808     
20809     content : {
20810         tag: 'tbody',
20811         cn: [
20812         {
20813             tag: 'tr',
20814             cn: [
20815             {
20816                 tag: 'td',
20817                 colspan: '7'
20818             }
20819             ]
20820         }
20821         ]
20822     },
20823     
20824     footer : {
20825         tag: 'tfoot',
20826         cn: [
20827         {
20828             tag: 'tr',
20829             cn: [
20830             {
20831                 tag: 'th',
20832                 colspan: '7',
20833                 cls: 'today'
20834             }
20835                     
20836             ]
20837         }
20838         ]
20839     },
20840     
20841     dates:{
20842         en: {
20843             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20844             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20845             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20846             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20847             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20848             today: "Today"
20849         }
20850     },
20851     
20852     modes: [
20853     {
20854         clsName: 'days',
20855         navFnc: 'Month',
20856         navStep: 1
20857     },
20858     {
20859         clsName: 'months',
20860         navFnc: 'FullYear',
20861         navStep: 1
20862     },
20863     {
20864         clsName: 'years',
20865         navFnc: 'FullYear',
20866         navStep: 10
20867     }]
20868 });
20869
20870 Roo.apply(Roo.bootstrap.DateField,  {
20871   
20872     template : {
20873         tag: 'div',
20874         cls: 'datepicker dropdown-menu roo-dynamic',
20875         cn: [
20876         {
20877             tag: 'div',
20878             cls: 'datepicker-days',
20879             cn: [
20880             {
20881                 tag: 'table',
20882                 cls: 'table-condensed',
20883                 cn:[
20884                 Roo.bootstrap.DateField.head,
20885                 {
20886                     tag: 'tbody'
20887                 },
20888                 Roo.bootstrap.DateField.footer
20889                 ]
20890             }
20891             ]
20892         },
20893         {
20894             tag: 'div',
20895             cls: 'datepicker-months',
20896             cn: [
20897             {
20898                 tag: 'table',
20899                 cls: 'table-condensed',
20900                 cn:[
20901                 Roo.bootstrap.DateField.head,
20902                 Roo.bootstrap.DateField.content,
20903                 Roo.bootstrap.DateField.footer
20904                 ]
20905             }
20906             ]
20907         },
20908         {
20909             tag: 'div',
20910             cls: 'datepicker-years',
20911             cn: [
20912             {
20913                 tag: 'table',
20914                 cls: 'table-condensed',
20915                 cn:[
20916                 Roo.bootstrap.DateField.head,
20917                 Roo.bootstrap.DateField.content,
20918                 Roo.bootstrap.DateField.footer
20919                 ]
20920             }
20921             ]
20922         }
20923         ]
20924     }
20925 });
20926
20927  
20928
20929  /*
20930  * - LGPL
20931  *
20932  * TimeField
20933  * 
20934  */
20935
20936 /**
20937  * @class Roo.bootstrap.TimeField
20938  * @extends Roo.bootstrap.Input
20939  * Bootstrap DateField class
20940  * 
20941  * 
20942  * @constructor
20943  * Create a new TimeField
20944  * @param {Object} config The config object
20945  */
20946
20947 Roo.bootstrap.TimeField = function(config){
20948     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20949     this.addEvents({
20950             /**
20951              * @event show
20952              * Fires when this field show.
20953              * @param {Roo.bootstrap.DateField} thisthis
20954              * @param {Mixed} date The date value
20955              */
20956             show : true,
20957             /**
20958              * @event show
20959              * Fires when this field hide.
20960              * @param {Roo.bootstrap.DateField} this
20961              * @param {Mixed} date The date value
20962              */
20963             hide : true,
20964             /**
20965              * @event select
20966              * Fires when select a date.
20967              * @param {Roo.bootstrap.DateField} this
20968              * @param {Mixed} date The date value
20969              */
20970             select : true
20971         });
20972 };
20973
20974 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20975     
20976     /**
20977      * @cfg {String} format
20978      * The default time format string which can be overriden for localization support.  The format must be
20979      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20980      */
20981     format : "H:i",
20982        
20983     onRender: function(ct, position)
20984     {
20985         
20986         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20987                 
20988         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20989         
20990         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20991         
20992         this.pop = this.picker().select('>.datepicker-time',true).first();
20993         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20994         
20995         this.picker().on('mousedown', this.onMousedown, this);
20996         this.picker().on('click', this.onClick, this);
20997         
20998         this.picker().addClass('datepicker-dropdown');
20999     
21000         this.fillTime();
21001         this.update();
21002             
21003         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21004         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21005         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21006         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21007         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21008         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21009
21010     },
21011     
21012     fireKey: function(e){
21013         if (!this.picker().isVisible()){
21014             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21015                 this.show();
21016             }
21017             return;
21018         }
21019
21020         e.preventDefault();
21021         
21022         switch(e.keyCode){
21023             case 27: // escape
21024                 this.hide();
21025                 break;
21026             case 37: // left
21027             case 39: // right
21028                 this.onTogglePeriod();
21029                 break;
21030             case 38: // up
21031                 this.onIncrementMinutes();
21032                 break;
21033             case 40: // down
21034                 this.onDecrementMinutes();
21035                 break;
21036             case 13: // enter
21037             case 9: // tab
21038                 this.setTime();
21039                 break;
21040         }
21041     },
21042     
21043     onClick: function(e) {
21044         e.stopPropagation();
21045         e.preventDefault();
21046     },
21047     
21048     picker : function()
21049     {
21050         return this.el.select('.datepicker', true).first();
21051     },
21052     
21053     fillTime: function()
21054     {    
21055         var time = this.pop.select('tbody', true).first();
21056         
21057         time.dom.innerHTML = '';
21058         
21059         time.createChild({
21060             tag: 'tr',
21061             cn: [
21062                 {
21063                     tag: 'td',
21064                     cn: [
21065                         {
21066                             tag: 'a',
21067                             href: '#',
21068                             cls: 'btn',
21069                             cn: [
21070                                 {
21071                                     tag: 'span',
21072                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21073                                 }
21074                             ]
21075                         } 
21076                     ]
21077                 },
21078                 {
21079                     tag: 'td',
21080                     cls: 'separator'
21081                 },
21082                 {
21083                     tag: 'td',
21084                     cn: [
21085                         {
21086                             tag: 'a',
21087                             href: '#',
21088                             cls: 'btn',
21089                             cn: [
21090                                 {
21091                                     tag: 'span',
21092                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21093                                 }
21094                             ]
21095                         }
21096                     ]
21097                 },
21098                 {
21099                     tag: 'td',
21100                     cls: 'separator'
21101                 }
21102             ]
21103         });
21104         
21105         time.createChild({
21106             tag: 'tr',
21107             cn: [
21108                 {
21109                     tag: 'td',
21110                     cn: [
21111                         {
21112                             tag: 'span',
21113                             cls: 'timepicker-hour',
21114                             html: '00'
21115                         }  
21116                     ]
21117                 },
21118                 {
21119                     tag: 'td',
21120                     cls: 'separator',
21121                     html: ':'
21122                 },
21123                 {
21124                     tag: 'td',
21125                     cn: [
21126                         {
21127                             tag: 'span',
21128                             cls: 'timepicker-minute',
21129                             html: '00'
21130                         }  
21131                     ]
21132                 },
21133                 {
21134                     tag: 'td',
21135                     cls: 'separator'
21136                 },
21137                 {
21138                     tag: 'td',
21139                     cn: [
21140                         {
21141                             tag: 'button',
21142                             type: 'button',
21143                             cls: 'btn btn-primary period',
21144                             html: 'AM'
21145                             
21146                         }
21147                     ]
21148                 }
21149             ]
21150         });
21151         
21152         time.createChild({
21153             tag: 'tr',
21154             cn: [
21155                 {
21156                     tag: 'td',
21157                     cn: [
21158                         {
21159                             tag: 'a',
21160                             href: '#',
21161                             cls: 'btn',
21162                             cn: [
21163                                 {
21164                                     tag: 'span',
21165                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21166                                 }
21167                             ]
21168                         }
21169                     ]
21170                 },
21171                 {
21172                     tag: 'td',
21173                     cls: 'separator'
21174                 },
21175                 {
21176                     tag: 'td',
21177                     cn: [
21178                         {
21179                             tag: 'a',
21180                             href: '#',
21181                             cls: 'btn',
21182                             cn: [
21183                                 {
21184                                     tag: 'span',
21185                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21186                                 }
21187                             ]
21188                         }
21189                     ]
21190                 },
21191                 {
21192                     tag: 'td',
21193                     cls: 'separator'
21194                 }
21195             ]
21196         });
21197         
21198     },
21199     
21200     update: function()
21201     {
21202         
21203         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21204         
21205         this.fill();
21206     },
21207     
21208     fill: function() 
21209     {
21210         var hours = this.time.getHours();
21211         var minutes = this.time.getMinutes();
21212         var period = 'AM';
21213         
21214         if(hours > 11){
21215             period = 'PM';
21216         }
21217         
21218         if(hours == 0){
21219             hours = 12;
21220         }
21221         
21222         
21223         if(hours > 12){
21224             hours = hours - 12;
21225         }
21226         
21227         if(hours < 10){
21228             hours = '0' + hours;
21229         }
21230         
21231         if(minutes < 10){
21232             minutes = '0' + minutes;
21233         }
21234         
21235         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21236         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21237         this.pop.select('button', true).first().dom.innerHTML = period;
21238         
21239     },
21240     
21241     place: function()
21242     {   
21243         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21244         
21245         var cls = ['bottom'];
21246         
21247         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21248             cls.pop();
21249             cls.push('top');
21250         }
21251         
21252         cls.push('right');
21253         
21254         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21255             cls.pop();
21256             cls.push('left');
21257         }
21258         
21259         this.picker().addClass(cls.join('-'));
21260         
21261         var _this = this;
21262         
21263         Roo.each(cls, function(c){
21264             if(c == 'bottom'){
21265                 _this.picker().setTop(_this.inputEl().getHeight());
21266                 return;
21267             }
21268             if(c == 'top'){
21269                 _this.picker().setTop(0 - _this.picker().getHeight());
21270                 return;
21271             }
21272             
21273             if(c == 'left'){
21274                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21275                 return;
21276             }
21277             if(c == 'right'){
21278                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21279                 return;
21280             }
21281         });
21282         
21283     },
21284   
21285     onFocus : function()
21286     {
21287         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21288         this.show();
21289     },
21290     
21291     onBlur : function()
21292     {
21293         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21294         this.hide();
21295     },
21296     
21297     show : function()
21298     {
21299         this.picker().show();
21300         this.pop.show();
21301         this.update();
21302         this.place();
21303         
21304         this.fireEvent('show', this, this.date);
21305     },
21306     
21307     hide : function()
21308     {
21309         this.picker().hide();
21310         this.pop.hide();
21311         
21312         this.fireEvent('hide', this, this.date);
21313     },
21314     
21315     setTime : function()
21316     {
21317         this.hide();
21318         this.setValue(this.time.format(this.format));
21319         
21320         this.fireEvent('select', this, this.date);
21321         
21322         
21323     },
21324     
21325     onMousedown: function(e){
21326         e.stopPropagation();
21327         e.preventDefault();
21328     },
21329     
21330     onIncrementHours: function()
21331     {
21332         Roo.log('onIncrementHours');
21333         this.time = this.time.add(Date.HOUR, 1);
21334         this.update();
21335         
21336     },
21337     
21338     onDecrementHours: function()
21339     {
21340         Roo.log('onDecrementHours');
21341         this.time = this.time.add(Date.HOUR, -1);
21342         this.update();
21343     },
21344     
21345     onIncrementMinutes: function()
21346     {
21347         Roo.log('onIncrementMinutes');
21348         this.time = this.time.add(Date.MINUTE, 1);
21349         this.update();
21350     },
21351     
21352     onDecrementMinutes: function()
21353     {
21354         Roo.log('onDecrementMinutes');
21355         this.time = this.time.add(Date.MINUTE, -1);
21356         this.update();
21357     },
21358     
21359     onTogglePeriod: function()
21360     {
21361         Roo.log('onTogglePeriod');
21362         this.time = this.time.add(Date.HOUR, 12);
21363         this.update();
21364     }
21365     
21366    
21367 });
21368
21369 Roo.apply(Roo.bootstrap.TimeField,  {
21370     
21371     content : {
21372         tag: 'tbody',
21373         cn: [
21374             {
21375                 tag: 'tr',
21376                 cn: [
21377                 {
21378                     tag: 'td',
21379                     colspan: '7'
21380                 }
21381                 ]
21382             }
21383         ]
21384     },
21385     
21386     footer : {
21387         tag: 'tfoot',
21388         cn: [
21389             {
21390                 tag: 'tr',
21391                 cn: [
21392                 {
21393                     tag: 'th',
21394                     colspan: '7',
21395                     cls: '',
21396                     cn: [
21397                         {
21398                             tag: 'button',
21399                             cls: 'btn btn-info ok',
21400                             html: 'OK'
21401                         }
21402                     ]
21403                 }
21404
21405                 ]
21406             }
21407         ]
21408     }
21409 });
21410
21411 Roo.apply(Roo.bootstrap.TimeField,  {
21412   
21413     template : {
21414         tag: 'div',
21415         cls: 'datepicker dropdown-menu',
21416         cn: [
21417             {
21418                 tag: 'div',
21419                 cls: 'datepicker-time',
21420                 cn: [
21421                 {
21422                     tag: 'table',
21423                     cls: 'table-condensed',
21424                     cn:[
21425                     Roo.bootstrap.TimeField.content,
21426                     Roo.bootstrap.TimeField.footer
21427                     ]
21428                 }
21429                 ]
21430             }
21431         ]
21432     }
21433 });
21434
21435  
21436
21437  /*
21438  * - LGPL
21439  *
21440  * MonthField
21441  * 
21442  */
21443
21444 /**
21445  * @class Roo.bootstrap.MonthField
21446  * @extends Roo.bootstrap.Input
21447  * Bootstrap MonthField class
21448  * 
21449  * @cfg {String} language default en
21450  * 
21451  * @constructor
21452  * Create a new MonthField
21453  * @param {Object} config The config object
21454  */
21455
21456 Roo.bootstrap.MonthField = function(config){
21457     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21458     
21459     this.addEvents({
21460         /**
21461          * @event show
21462          * Fires when this field show.
21463          * @param {Roo.bootstrap.MonthField} this
21464          * @param {Mixed} date The date value
21465          */
21466         show : true,
21467         /**
21468          * @event show
21469          * Fires when this field hide.
21470          * @param {Roo.bootstrap.MonthField} this
21471          * @param {Mixed} date The date value
21472          */
21473         hide : true,
21474         /**
21475          * @event select
21476          * Fires when select a date.
21477          * @param {Roo.bootstrap.MonthField} this
21478          * @param {String} oldvalue The old value
21479          * @param {String} newvalue The new value
21480          */
21481         select : true
21482     });
21483 };
21484
21485 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21486     
21487     onRender: function(ct, position)
21488     {
21489         
21490         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21491         
21492         this.language = this.language || 'en';
21493         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21494         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21495         
21496         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21497         this.isInline = false;
21498         this.isInput = true;
21499         this.component = this.el.select('.add-on', true).first() || false;
21500         this.component = (this.component && this.component.length === 0) ? false : this.component;
21501         this.hasInput = this.component && this.inputEL().length;
21502         
21503         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21504         
21505         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21506         
21507         this.picker().on('mousedown', this.onMousedown, this);
21508         this.picker().on('click', this.onClick, this);
21509         
21510         this.picker().addClass('datepicker-dropdown');
21511         
21512         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21513             v.setStyle('width', '189px');
21514         });
21515         
21516         this.fillMonths();
21517         
21518         this.update();
21519         
21520         if(this.isInline) {
21521             this.show();
21522         }
21523         
21524     },
21525     
21526     setValue: function(v, suppressEvent)
21527     {   
21528         var o = this.getValue();
21529         
21530         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21531         
21532         this.update();
21533
21534         if(suppressEvent !== true){
21535             this.fireEvent('select', this, o, v);
21536         }
21537         
21538     },
21539     
21540     getValue: function()
21541     {
21542         return this.value;
21543     },
21544     
21545     onClick: function(e) 
21546     {
21547         e.stopPropagation();
21548         e.preventDefault();
21549         
21550         var target = e.getTarget();
21551         
21552         if(target.nodeName.toLowerCase() === 'i'){
21553             target = Roo.get(target).dom.parentNode;
21554         }
21555         
21556         var nodeName = target.nodeName;
21557         var className = target.className;
21558         var html = target.innerHTML;
21559         
21560         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21561             return;
21562         }
21563         
21564         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21565         
21566         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21567         
21568         this.hide();
21569                         
21570     },
21571     
21572     picker : function()
21573     {
21574         return this.pickerEl;
21575     },
21576     
21577     fillMonths: function()
21578     {    
21579         var i = 0;
21580         var months = this.picker().select('>.datepicker-months td', true).first();
21581         
21582         months.dom.innerHTML = '';
21583         
21584         while (i < 12) {
21585             var month = {
21586                 tag: 'span',
21587                 cls: 'month',
21588                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21589             };
21590             
21591             months.createChild(month);
21592         }
21593         
21594     },
21595     
21596     update: function()
21597     {
21598         var _this = this;
21599         
21600         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21601             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21602         }
21603         
21604         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21605             e.removeClass('active');
21606             
21607             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21608                 e.addClass('active');
21609             }
21610         })
21611     },
21612     
21613     place: function()
21614     {
21615         if(this.isInline) {
21616             return;
21617         }
21618         
21619         this.picker().removeClass(['bottom', 'top']);
21620         
21621         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21622             /*
21623              * place to the top of element!
21624              *
21625              */
21626             
21627             this.picker().addClass('top');
21628             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21629             
21630             return;
21631         }
21632         
21633         this.picker().addClass('bottom');
21634         
21635         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21636     },
21637     
21638     onFocus : function()
21639     {
21640         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21641         this.show();
21642     },
21643     
21644     onBlur : function()
21645     {
21646         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21647         
21648         var d = this.inputEl().getValue();
21649         
21650         this.setValue(d);
21651                 
21652         this.hide();
21653     },
21654     
21655     show : function()
21656     {
21657         this.picker().show();
21658         this.picker().select('>.datepicker-months', true).first().show();
21659         this.update();
21660         this.place();
21661         
21662         this.fireEvent('show', this, this.date);
21663     },
21664     
21665     hide : function()
21666     {
21667         if(this.isInline) {
21668             return;
21669         }
21670         this.picker().hide();
21671         this.fireEvent('hide', this, this.date);
21672         
21673     },
21674     
21675     onMousedown: function(e)
21676     {
21677         e.stopPropagation();
21678         e.preventDefault();
21679     },
21680     
21681     keyup: function(e)
21682     {
21683         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21684         this.update();
21685     },
21686
21687     fireKey: function(e)
21688     {
21689         if (!this.picker().isVisible()){
21690             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21691                 this.show();
21692             }
21693             return;
21694         }
21695         
21696         var dir;
21697         
21698         switch(e.keyCode){
21699             case 27: // escape
21700                 this.hide();
21701                 e.preventDefault();
21702                 break;
21703             case 37: // left
21704             case 39: // right
21705                 dir = e.keyCode == 37 ? -1 : 1;
21706                 
21707                 this.vIndex = this.vIndex + dir;
21708                 
21709                 if(this.vIndex < 0){
21710                     this.vIndex = 0;
21711                 }
21712                 
21713                 if(this.vIndex > 11){
21714                     this.vIndex = 11;
21715                 }
21716                 
21717                 if(isNaN(this.vIndex)){
21718                     this.vIndex = 0;
21719                 }
21720                 
21721                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21722                 
21723                 break;
21724             case 38: // up
21725             case 40: // down
21726                 
21727                 dir = e.keyCode == 38 ? -1 : 1;
21728                 
21729                 this.vIndex = this.vIndex + dir * 4;
21730                 
21731                 if(this.vIndex < 0){
21732                     this.vIndex = 0;
21733                 }
21734                 
21735                 if(this.vIndex > 11){
21736                     this.vIndex = 11;
21737                 }
21738                 
21739                 if(isNaN(this.vIndex)){
21740                     this.vIndex = 0;
21741                 }
21742                 
21743                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21744                 break;
21745                 
21746             case 13: // enter
21747                 
21748                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21749                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21750                 }
21751                 
21752                 this.hide();
21753                 e.preventDefault();
21754                 break;
21755             case 9: // tab
21756                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21757                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21758                 }
21759                 this.hide();
21760                 break;
21761             case 16: // shift
21762             case 17: // ctrl
21763             case 18: // alt
21764                 break;
21765             default :
21766                 this.hide();
21767                 
21768         }
21769     },
21770     
21771     remove: function() 
21772     {
21773         this.picker().remove();
21774     }
21775    
21776 });
21777
21778 Roo.apply(Roo.bootstrap.MonthField,  {
21779     
21780     content : {
21781         tag: 'tbody',
21782         cn: [
21783         {
21784             tag: 'tr',
21785             cn: [
21786             {
21787                 tag: 'td',
21788                 colspan: '7'
21789             }
21790             ]
21791         }
21792         ]
21793     },
21794     
21795     dates:{
21796         en: {
21797             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21798             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21799         }
21800     }
21801 });
21802
21803 Roo.apply(Roo.bootstrap.MonthField,  {
21804   
21805     template : {
21806         tag: 'div',
21807         cls: 'datepicker dropdown-menu roo-dynamic',
21808         cn: [
21809             {
21810                 tag: 'div',
21811                 cls: 'datepicker-months',
21812                 cn: [
21813                 {
21814                     tag: 'table',
21815                     cls: 'table-condensed',
21816                     cn:[
21817                         Roo.bootstrap.DateField.content
21818                     ]
21819                 }
21820                 ]
21821             }
21822         ]
21823     }
21824 });
21825
21826  
21827
21828  
21829  /*
21830  * - LGPL
21831  *
21832  * CheckBox
21833  * 
21834  */
21835
21836 /**
21837  * @class Roo.bootstrap.CheckBox
21838  * @extends Roo.bootstrap.Input
21839  * Bootstrap CheckBox class
21840  * 
21841  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21842  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21843  * @cfg {String} boxLabel The text that appears beside the checkbox
21844  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21845  * @cfg {Boolean} checked initnal the element
21846  * @cfg {Boolean} inline inline the element (default false)
21847  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21848  * @cfg {String} tooltip label tooltip
21849  * 
21850  * @constructor
21851  * Create a new CheckBox
21852  * @param {Object} config The config object
21853  */
21854
21855 Roo.bootstrap.CheckBox = function(config){
21856     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21857    
21858     this.addEvents({
21859         /**
21860         * @event check
21861         * Fires when the element is checked or unchecked.
21862         * @param {Roo.bootstrap.CheckBox} this This input
21863         * @param {Boolean} checked The new checked value
21864         */
21865        check : true,
21866        /**
21867         * @event click
21868         * Fires when the element is click.
21869         * @param {Roo.bootstrap.CheckBox} this This input
21870         */
21871        click : true
21872     });
21873     
21874 };
21875
21876 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21877   
21878     inputType: 'checkbox',
21879     inputValue: 1,
21880     valueOff: 0,
21881     boxLabel: false,
21882     checked: false,
21883     weight : false,
21884     inline: false,
21885     tooltip : '',
21886     
21887     // checkbox success does not make any sense really.. 
21888     invalidClass : "",
21889     validClass : "",
21890     
21891     
21892     getAutoCreate : function()
21893     {
21894         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21895         
21896         var id = Roo.id();
21897         
21898         var cfg = {};
21899         
21900         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21901         
21902         if(this.inline){
21903             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21904         }
21905         
21906         var input =  {
21907             tag: 'input',
21908             id : id,
21909             type : this.inputType,
21910             value : this.inputValue,
21911             cls : 'roo-' + this.inputType, //'form-box',
21912             placeholder : this.placeholder || ''
21913             
21914         };
21915         
21916         if(this.inputType != 'radio'){
21917             var hidden =  {
21918                 tag: 'input',
21919                 type : 'hidden',
21920                 cls : 'roo-hidden-value',
21921                 value : this.checked ? this.inputValue : this.valueOff
21922             };
21923         }
21924         
21925             
21926         if (this.weight) { // Validity check?
21927             cfg.cls += " " + this.inputType + "-" + this.weight;
21928         }
21929         
21930         if (this.disabled) {
21931             input.disabled=true;
21932         }
21933         
21934         if(this.checked){
21935             input.checked = this.checked;
21936         }
21937         
21938         if (this.name) {
21939             
21940             input.name = this.name;
21941             
21942             if(this.inputType != 'radio'){
21943                 hidden.name = this.name;
21944                 input.name = '_hidden_' + this.name;
21945             }
21946         }
21947         
21948         if (this.size) {
21949             input.cls += ' input-' + this.size;
21950         }
21951         
21952         var settings=this;
21953         
21954         ['xs','sm','md','lg'].map(function(size){
21955             if (settings[size]) {
21956                 cfg.cls += ' col-' + size + '-' + settings[size];
21957             }
21958         });
21959         
21960         var inputblock = input;
21961          
21962         if (this.before || this.after) {
21963             
21964             inputblock = {
21965                 cls : 'input-group',
21966                 cn :  [] 
21967             };
21968             
21969             if (this.before) {
21970                 inputblock.cn.push({
21971                     tag :'span',
21972                     cls : 'input-group-addon',
21973                     html : this.before
21974                 });
21975             }
21976             
21977             inputblock.cn.push(input);
21978             
21979             if(this.inputType != 'radio'){
21980                 inputblock.cn.push(hidden);
21981             }
21982             
21983             if (this.after) {
21984                 inputblock.cn.push({
21985                     tag :'span',
21986                     cls : 'input-group-addon',
21987                     html : this.after
21988                 });
21989             }
21990             
21991         }
21992         var boxLabelCfg = false;
21993         
21994         if(this.boxLabel){
21995            
21996             boxLabelCfg = {
21997                 tag: 'label',
21998                 //'for': id, // box label is handled by onclick - so no for...
21999                 cls: 'box-label',
22000                 html: this.boxLabel
22001             };
22002             if(this.tooltip){
22003                 boxLabelCfg.tooltip = this.tooltip;
22004             }
22005              
22006         }
22007         
22008         
22009         if (align ==='left' && this.fieldLabel.length) {
22010 //                Roo.log("left and has label");
22011             cfg.cn = [
22012                 {
22013                     tag: 'label',
22014                     'for' :  id,
22015                     cls : 'control-label',
22016                     html : this.fieldLabel
22017                 },
22018                 {
22019                     cls : "", 
22020                     cn: [
22021                         inputblock
22022                     ]
22023                 }
22024             ];
22025             
22026             if (boxLabelCfg) {
22027                 cfg.cn[1].cn.push(boxLabelCfg);
22028             }
22029             
22030             if(this.labelWidth > 12){
22031                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22032             }
22033             
22034             if(this.labelWidth < 13 && this.labelmd == 0){
22035                 this.labelmd = this.labelWidth;
22036             }
22037             
22038             if(this.labellg > 0){
22039                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22040                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22041             }
22042             
22043             if(this.labelmd > 0){
22044                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22045                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22046             }
22047             
22048             if(this.labelsm > 0){
22049                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22050                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22051             }
22052             
22053             if(this.labelxs > 0){
22054                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22055                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22056             }
22057             
22058         } else if ( this.fieldLabel.length) {
22059 //                Roo.log(" label");
22060                 cfg.cn = [
22061                    
22062                     {
22063                         tag: this.boxLabel ? 'span' : 'label',
22064                         'for': id,
22065                         cls: 'control-label box-input-label',
22066                         //cls : 'input-group-addon',
22067                         html : this.fieldLabel
22068                     },
22069                     
22070                     inputblock
22071                     
22072                 ];
22073                 if (boxLabelCfg) {
22074                     cfg.cn.push(boxLabelCfg);
22075                 }
22076
22077         } else {
22078             
22079 //                Roo.log(" no label && no align");
22080                 cfg.cn = [  inputblock ] ;
22081                 if (boxLabelCfg) {
22082                     cfg.cn.push(boxLabelCfg);
22083                 }
22084
22085                 
22086         }
22087         
22088        
22089         
22090         if(this.inputType != 'radio'){
22091             cfg.cn.push(hidden);
22092         }
22093         
22094         return cfg;
22095         
22096     },
22097     
22098     /**
22099      * return the real input element.
22100      */
22101     inputEl: function ()
22102     {
22103         return this.el.select('input.roo-' + this.inputType,true).first();
22104     },
22105     hiddenEl: function ()
22106     {
22107         return this.el.select('input.roo-hidden-value',true).first();
22108     },
22109     
22110     labelEl: function()
22111     {
22112         return this.el.select('label.control-label',true).first();
22113     },
22114     /* depricated... */
22115     
22116     label: function()
22117     {
22118         return this.labelEl();
22119     },
22120     
22121     boxLabelEl: function()
22122     {
22123         return this.el.select('label.box-label',true).first();
22124     },
22125     
22126     initEvents : function()
22127     {
22128 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22129         
22130         this.inputEl().on('click', this.onClick,  this);
22131         
22132         if (this.boxLabel) { 
22133             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22134         }
22135         
22136         this.startValue = this.getValue();
22137         
22138         if(this.groupId){
22139             Roo.bootstrap.CheckBox.register(this);
22140         }
22141     },
22142     
22143     onClick : function(e)
22144     {   
22145         if(this.fireEvent('click', this, e) !== false){
22146             this.setChecked(!this.checked);
22147         }
22148         
22149     },
22150     
22151     setChecked : function(state,suppressEvent)
22152     {
22153         this.startValue = this.getValue();
22154
22155         if(this.inputType == 'radio'){
22156             
22157             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22158                 e.dom.checked = false;
22159             });
22160             
22161             this.inputEl().dom.checked = true;
22162             
22163             this.inputEl().dom.value = this.inputValue;
22164             
22165             if(suppressEvent !== true){
22166                 this.fireEvent('check', this, true);
22167             }
22168             
22169             this.validate();
22170             
22171             return;
22172         }
22173         
22174         this.checked = state;
22175         
22176         this.inputEl().dom.checked = state;
22177         
22178         
22179         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22180         
22181         if(suppressEvent !== true){
22182             this.fireEvent('check', this, state);
22183         }
22184         
22185         this.validate();
22186     },
22187     
22188     getValue : function()
22189     {
22190         if(this.inputType == 'radio'){
22191             return this.getGroupValue();
22192         }
22193         
22194         return this.hiddenEl().dom.value;
22195         
22196     },
22197     
22198     getGroupValue : function()
22199     {
22200         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22201             return '';
22202         }
22203         
22204         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22205     },
22206     
22207     setValue : function(v,suppressEvent)
22208     {
22209         if(this.inputType == 'radio'){
22210             this.setGroupValue(v, suppressEvent);
22211             return;
22212         }
22213         
22214         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22215         
22216         this.validate();
22217     },
22218     
22219     setGroupValue : function(v, suppressEvent)
22220     {
22221         this.startValue = this.getValue();
22222         
22223         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22224             e.dom.checked = false;
22225             
22226             if(e.dom.value == v){
22227                 e.dom.checked = true;
22228             }
22229         });
22230         
22231         if(suppressEvent !== true){
22232             this.fireEvent('check', this, true);
22233         }
22234
22235         this.validate();
22236         
22237         return;
22238     },
22239     
22240     validate : function()
22241     {
22242         if(this.getVisibilityEl().hasClass('hidden')){
22243             return true;
22244         }
22245         
22246         if(
22247                 this.disabled || 
22248                 (this.inputType == 'radio' && this.validateRadio()) ||
22249                 (this.inputType == 'checkbox' && this.validateCheckbox())
22250         ){
22251             this.markValid();
22252             return true;
22253         }
22254         
22255         this.markInvalid();
22256         return false;
22257     },
22258     
22259     validateRadio : function()
22260     {
22261         if(this.getVisibilityEl().hasClass('hidden')){
22262             return true;
22263         }
22264         
22265         if(this.allowBlank){
22266             return true;
22267         }
22268         
22269         var valid = false;
22270         
22271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22272             if(!e.dom.checked){
22273                 return;
22274             }
22275             
22276             valid = true;
22277             
22278             return false;
22279         });
22280         
22281         return valid;
22282     },
22283     
22284     validateCheckbox : function()
22285     {
22286         if(!this.groupId){
22287             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22288             //return (this.getValue() == this.inputValue) ? true : false;
22289         }
22290         
22291         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22292         
22293         if(!group){
22294             return false;
22295         }
22296         
22297         var r = false;
22298         
22299         for(var i in group){
22300             if(group[i].el.isVisible(true)){
22301                 r = false;
22302                 break;
22303             }
22304             
22305             r = true;
22306         }
22307         
22308         for(var i in group){
22309             if(r){
22310                 break;
22311             }
22312             
22313             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22314         }
22315         
22316         return r;
22317     },
22318     
22319     /**
22320      * Mark this field as valid
22321      */
22322     markValid : function()
22323     {
22324         var _this = this;
22325         
22326         this.fireEvent('valid', this);
22327         
22328         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22329         
22330         if(this.groupId){
22331             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22332         }
22333         
22334         if(label){
22335             label.markValid();
22336         }
22337
22338         if(this.inputType == 'radio'){
22339             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22340                 var fg = e.findParent('.form-group', false, true);
22341                 if (Roo.bootstrap.version == 3) {
22342                     fg.removeClass([_this.invalidClass, _this.validClass]);
22343                     fg.addClass(_this.validClass);
22344                 } else {
22345                     fg.removeClass(['is-valid', 'is-invalid']);
22346                     fg.addClass('is-valid');
22347                 }
22348             });
22349             
22350             return;
22351         }
22352
22353         if(!this.groupId){
22354             var fg = this.el.findParent('.form-group', false, true);
22355             if (Roo.bootstrap.version == 3) {
22356                 fg.removeClass([this.invalidClass, this.validClass]);
22357                 fg.addClass(this.validClass);
22358             } else {
22359                 fg.removeClass(['is-valid', 'is-invalid']);
22360                 fg.addClass('is-valid');
22361             }
22362             return;
22363         }
22364         
22365         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22366         
22367         if(!group){
22368             return;
22369         }
22370         
22371         for(var i in group){
22372             var fg = group[i].el.findParent('.form-group', false, true);
22373             if (Roo.bootstrap.version == 3) {
22374                 fg.removeClass([this.invalidClass, this.validClass]);
22375                 fg.addClass(this.validClass);
22376             } else {
22377                 fg.removeClass(['is-valid', 'is-invalid']);
22378                 fg.addClass('is-valid');
22379             }
22380         }
22381     },
22382     
22383      /**
22384      * Mark this field as invalid
22385      * @param {String} msg The validation message
22386      */
22387     markInvalid : function(msg)
22388     {
22389         if(this.allowBlank){
22390             return;
22391         }
22392         
22393         var _this = this;
22394         
22395         this.fireEvent('invalid', this, msg);
22396         
22397         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22398         
22399         if(this.groupId){
22400             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22401         }
22402         
22403         if(label){
22404             label.markInvalid();
22405         }
22406             
22407         if(this.inputType == 'radio'){
22408             
22409             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22410                 var fg = e.findParent('.form-group', false, true);
22411                 if (Roo.bootstrap.version == 3) {
22412                     fg.removeClass([_this.invalidClass, _this.validClass]);
22413                     fg.addClass(_this.invalidClass);
22414                 } else {
22415                     fg.removeClass(['is-invalid', 'is-valid']);
22416                     fg.addClass('is-invalid');
22417                 }
22418             });
22419             
22420             return;
22421         }
22422         
22423         if(!this.groupId){
22424             var fg = this.el.findParent('.form-group', false, true);
22425             if (Roo.bootstrap.version == 3) {
22426                 fg.removeClass([_this.invalidClass, _this.validClass]);
22427                 fg.addClass(_this.invalidClass);
22428             } else {
22429                 fg.removeClass(['is-invalid', 'is-valid']);
22430                 fg.addClass('is-invalid');
22431             }
22432             return;
22433         }
22434         
22435         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22436         
22437         if(!group){
22438             return;
22439         }
22440         
22441         for(var i in group){
22442             var fg = group[i].el.findParent('.form-group', false, true);
22443             if (Roo.bootstrap.version == 3) {
22444                 fg.removeClass([_this.invalidClass, _this.validClass]);
22445                 fg.addClass(_this.invalidClass);
22446             } else {
22447                 fg.removeClass(['is-invalid', 'is-valid']);
22448                 fg.addClass('is-invalid');
22449             }
22450         }
22451         
22452     },
22453     
22454     clearInvalid : function()
22455     {
22456         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22457         
22458         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22459         
22460         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22461         
22462         if (label && label.iconEl) {
22463             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22464             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22465         }
22466     },
22467     
22468     disable : function()
22469     {
22470         if(this.inputType != 'radio'){
22471             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22472             return;
22473         }
22474         
22475         var _this = this;
22476         
22477         if(this.rendered){
22478             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22479                 _this.getActionEl().addClass(this.disabledClass);
22480                 e.dom.disabled = true;
22481             });
22482         }
22483         
22484         this.disabled = true;
22485         this.fireEvent("disable", this);
22486         return this;
22487     },
22488
22489     enable : function()
22490     {
22491         if(this.inputType != 'radio'){
22492             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22493             return;
22494         }
22495         
22496         var _this = this;
22497         
22498         if(this.rendered){
22499             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22500                 _this.getActionEl().removeClass(this.disabledClass);
22501                 e.dom.disabled = false;
22502             });
22503         }
22504         
22505         this.disabled = false;
22506         this.fireEvent("enable", this);
22507         return this;
22508     },
22509     
22510     setBoxLabel : function(v)
22511     {
22512         this.boxLabel = v;
22513         
22514         if(this.rendered){
22515             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22516         }
22517     }
22518
22519 });
22520
22521 Roo.apply(Roo.bootstrap.CheckBox, {
22522     
22523     groups: {},
22524     
22525      /**
22526     * register a CheckBox Group
22527     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22528     */
22529     register : function(checkbox)
22530     {
22531         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22532             this.groups[checkbox.groupId] = {};
22533         }
22534         
22535         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22536             return;
22537         }
22538         
22539         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22540         
22541     },
22542     /**
22543     * fetch a CheckBox Group based on the group ID
22544     * @param {string} the group ID
22545     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22546     */
22547     get: function(groupId) {
22548         if (typeof(this.groups[groupId]) == 'undefined') {
22549             return false;
22550         }
22551         
22552         return this.groups[groupId] ;
22553     }
22554     
22555     
22556 });
22557 /*
22558  * - LGPL
22559  *
22560  * RadioItem
22561  * 
22562  */
22563
22564 /**
22565  * @class Roo.bootstrap.Radio
22566  * @extends Roo.bootstrap.Component
22567  * Bootstrap Radio class
22568  * @cfg {String} boxLabel - the label associated
22569  * @cfg {String} value - the value of radio
22570  * 
22571  * @constructor
22572  * Create a new Radio
22573  * @param {Object} config The config object
22574  */
22575 Roo.bootstrap.Radio = function(config){
22576     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22577     
22578 };
22579
22580 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22581     
22582     boxLabel : '',
22583     
22584     value : '',
22585     
22586     getAutoCreate : function()
22587     {
22588         var cfg = {
22589             tag : 'div',
22590             cls : 'form-group radio',
22591             cn : [
22592                 {
22593                     tag : 'label',
22594                     cls : 'box-label',
22595                     html : this.boxLabel
22596                 }
22597             ]
22598         };
22599         
22600         return cfg;
22601     },
22602     
22603     initEvents : function() 
22604     {
22605         this.parent().register(this);
22606         
22607         this.el.on('click', this.onClick, this);
22608         
22609     },
22610     
22611     onClick : function(e)
22612     {
22613         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22614             this.setChecked(true);
22615         }
22616     },
22617     
22618     setChecked : function(state, suppressEvent)
22619     {
22620         this.parent().setValue(this.value, suppressEvent);
22621         
22622     },
22623     
22624     setBoxLabel : function(v)
22625     {
22626         this.boxLabel = v;
22627         
22628         if(this.rendered){
22629             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22630         }
22631     }
22632     
22633 });
22634  
22635
22636  /*
22637  * - LGPL
22638  *
22639  * Input
22640  * 
22641  */
22642
22643 /**
22644  * @class Roo.bootstrap.SecurePass
22645  * @extends Roo.bootstrap.Input
22646  * Bootstrap SecurePass class
22647  *
22648  * 
22649  * @constructor
22650  * Create a new SecurePass
22651  * @param {Object} config The config object
22652  */
22653  
22654 Roo.bootstrap.SecurePass = function (config) {
22655     // these go here, so the translation tool can replace them..
22656     this.errors = {
22657         PwdEmpty: "Please type a password, and then retype it to confirm.",
22658         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22659         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22660         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22661         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22662         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22663         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22664         TooWeak: "Your password is Too Weak."
22665     },
22666     this.meterLabel = "Password strength:";
22667     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22668     this.meterClass = [
22669         "roo-password-meter-tooweak", 
22670         "roo-password-meter-weak", 
22671         "roo-password-meter-medium", 
22672         "roo-password-meter-strong", 
22673         "roo-password-meter-grey"
22674     ];
22675     
22676     this.errors = {};
22677     
22678     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22679 }
22680
22681 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22682     /**
22683      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22684      * {
22685      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22686      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22687      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22688      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22689      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22690      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22691      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22692      * })
22693      */
22694     // private
22695     
22696     meterWidth: 300,
22697     errorMsg :'',    
22698     errors: false,
22699     imageRoot: '/',
22700     /**
22701      * @cfg {String/Object} Label for the strength meter (defaults to
22702      * 'Password strength:')
22703      */
22704     // private
22705     meterLabel: '',
22706     /**
22707      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22708      * ['Weak', 'Medium', 'Strong'])
22709      */
22710     // private    
22711     pwdStrengths: false,    
22712     // private
22713     strength: 0,
22714     // private
22715     _lastPwd: null,
22716     // private
22717     kCapitalLetter: 0,
22718     kSmallLetter: 1,
22719     kDigit: 2,
22720     kPunctuation: 3,
22721     
22722     insecure: false,
22723     // private
22724     initEvents: function ()
22725     {
22726         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22727
22728         if (this.el.is('input[type=password]') && Roo.isSafari) {
22729             this.el.on('keydown', this.SafariOnKeyDown, this);
22730         }
22731
22732         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22733     },
22734     // private
22735     onRender: function (ct, position)
22736     {
22737         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22738         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22739         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22740
22741         this.trigger.createChild({
22742                    cn: [
22743                     {
22744                     //id: 'PwdMeter',
22745                     tag: 'div',
22746                     cls: 'roo-password-meter-grey col-xs-12',
22747                     style: {
22748                         //width: 0,
22749                         //width: this.meterWidth + 'px'                                                
22750                         }
22751                     },
22752                     {                            
22753                          cls: 'roo-password-meter-text'                          
22754                     }
22755                 ]            
22756         });
22757
22758          
22759         if (this.hideTrigger) {
22760             this.trigger.setDisplayed(false);
22761         }
22762         this.setSize(this.width || '', this.height || '');
22763     },
22764     // private
22765     onDestroy: function ()
22766     {
22767         if (this.trigger) {
22768             this.trigger.removeAllListeners();
22769             this.trigger.remove();
22770         }
22771         if (this.wrap) {
22772             this.wrap.remove();
22773         }
22774         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22775     },
22776     // private
22777     checkStrength: function ()
22778     {
22779         var pwd = this.inputEl().getValue();
22780         if (pwd == this._lastPwd) {
22781             return;
22782         }
22783
22784         var strength;
22785         if (this.ClientSideStrongPassword(pwd)) {
22786             strength = 3;
22787         } else if (this.ClientSideMediumPassword(pwd)) {
22788             strength = 2;
22789         } else if (this.ClientSideWeakPassword(pwd)) {
22790             strength = 1;
22791         } else {
22792             strength = 0;
22793         }
22794         
22795         Roo.log('strength1: ' + strength);
22796         
22797         //var pm = this.trigger.child('div/div/div').dom;
22798         var pm = this.trigger.child('div/div');
22799         pm.removeClass(this.meterClass);
22800         pm.addClass(this.meterClass[strength]);
22801                 
22802         
22803         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22804                 
22805         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22806         
22807         this._lastPwd = pwd;
22808     },
22809     reset: function ()
22810     {
22811         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22812         
22813         this._lastPwd = '';
22814         
22815         var pm = this.trigger.child('div/div');
22816         pm.removeClass(this.meterClass);
22817         pm.addClass('roo-password-meter-grey');        
22818         
22819         
22820         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22821         
22822         pt.innerHTML = '';
22823         this.inputEl().dom.type='password';
22824     },
22825     // private
22826     validateValue: function (value)
22827     {
22828         
22829         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22830             return false;
22831         }
22832         if (value.length == 0) {
22833             if (this.allowBlank) {
22834                 this.clearInvalid();
22835                 return true;
22836             }
22837
22838             this.markInvalid(this.errors.PwdEmpty);
22839             this.errorMsg = this.errors.PwdEmpty;
22840             return false;
22841         }
22842         
22843         if(this.insecure){
22844             return true;
22845         }
22846         
22847         if ('[\x21-\x7e]*'.match(value)) {
22848             this.markInvalid(this.errors.PwdBadChar);
22849             this.errorMsg = this.errors.PwdBadChar;
22850             return false;
22851         }
22852         if (value.length < 6) {
22853             this.markInvalid(this.errors.PwdShort);
22854             this.errorMsg = this.errors.PwdShort;
22855             return false;
22856         }
22857         if (value.length > 16) {
22858             this.markInvalid(this.errors.PwdLong);
22859             this.errorMsg = this.errors.PwdLong;
22860             return false;
22861         }
22862         var strength;
22863         if (this.ClientSideStrongPassword(value)) {
22864             strength = 3;
22865         } else if (this.ClientSideMediumPassword(value)) {
22866             strength = 2;
22867         } else if (this.ClientSideWeakPassword(value)) {
22868             strength = 1;
22869         } else {
22870             strength = 0;
22871         }
22872
22873         
22874         if (strength < 2) {
22875             //this.markInvalid(this.errors.TooWeak);
22876             this.errorMsg = this.errors.TooWeak;
22877             //return false;
22878         }
22879         
22880         
22881         console.log('strength2: ' + strength);
22882         
22883         //var pm = this.trigger.child('div/div/div').dom;
22884         
22885         var pm = this.trigger.child('div/div');
22886         pm.removeClass(this.meterClass);
22887         pm.addClass(this.meterClass[strength]);
22888                 
22889         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22890                 
22891         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22892         
22893         this.errorMsg = ''; 
22894         return true;
22895     },
22896     // private
22897     CharacterSetChecks: function (type)
22898     {
22899         this.type = type;
22900         this.fResult = false;
22901     },
22902     // private
22903     isctype: function (character, type)
22904     {
22905         switch (type) {  
22906             case this.kCapitalLetter:
22907                 if (character >= 'A' && character <= 'Z') {
22908                     return true;
22909                 }
22910                 break;
22911             
22912             case this.kSmallLetter:
22913                 if (character >= 'a' && character <= 'z') {
22914                     return true;
22915                 }
22916                 break;
22917             
22918             case this.kDigit:
22919                 if (character >= '0' && character <= '9') {
22920                     return true;
22921                 }
22922                 break;
22923             
22924             case this.kPunctuation:
22925                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22926                     return true;
22927                 }
22928                 break;
22929             
22930             default:
22931                 return false;
22932         }
22933
22934     },
22935     // private
22936     IsLongEnough: function (pwd, size)
22937     {
22938         return !(pwd == null || isNaN(size) || pwd.length < size);
22939     },
22940     // private
22941     SpansEnoughCharacterSets: function (word, nb)
22942     {
22943         if (!this.IsLongEnough(word, nb))
22944         {
22945             return false;
22946         }
22947
22948         var characterSetChecks = new Array(
22949             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22950             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22951         );
22952         
22953         for (var index = 0; index < word.length; ++index) {
22954             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22955                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22956                     characterSetChecks[nCharSet].fResult = true;
22957                     break;
22958                 }
22959             }
22960         }
22961
22962         var nCharSets = 0;
22963         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22964             if (characterSetChecks[nCharSet].fResult) {
22965                 ++nCharSets;
22966             }
22967         }
22968
22969         if (nCharSets < nb) {
22970             return false;
22971         }
22972         return true;
22973     },
22974     // private
22975     ClientSideStrongPassword: function (pwd)
22976     {
22977         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22978     },
22979     // private
22980     ClientSideMediumPassword: function (pwd)
22981     {
22982         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22983     },
22984     // private
22985     ClientSideWeakPassword: function (pwd)
22986     {
22987         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22988     }
22989           
22990 })//<script type="text/javascript">
22991
22992 /*
22993  * Based  Ext JS Library 1.1.1
22994  * Copyright(c) 2006-2007, Ext JS, LLC.
22995  * LGPL
22996  *
22997  */
22998  
22999 /**
23000  * @class Roo.HtmlEditorCore
23001  * @extends Roo.Component
23002  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23003  *
23004  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23005  */
23006
23007 Roo.HtmlEditorCore = function(config){
23008     
23009     
23010     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23011     
23012     
23013     this.addEvents({
23014         /**
23015          * @event initialize
23016          * Fires when the editor is fully initialized (including the iframe)
23017          * @param {Roo.HtmlEditorCore} this
23018          */
23019         initialize: true,
23020         /**
23021          * @event activate
23022          * Fires when the editor is first receives the focus. Any insertion must wait
23023          * until after this event.
23024          * @param {Roo.HtmlEditorCore} this
23025          */
23026         activate: true,
23027          /**
23028          * @event beforesync
23029          * Fires before the textarea is updated with content from the editor iframe. Return false
23030          * to cancel the sync.
23031          * @param {Roo.HtmlEditorCore} this
23032          * @param {String} html
23033          */
23034         beforesync: true,
23035          /**
23036          * @event beforepush
23037          * Fires before the iframe editor is updated with content from the textarea. Return false
23038          * to cancel the push.
23039          * @param {Roo.HtmlEditorCore} this
23040          * @param {String} html
23041          */
23042         beforepush: true,
23043          /**
23044          * @event sync
23045          * Fires when the textarea is updated with content from the editor iframe.
23046          * @param {Roo.HtmlEditorCore} this
23047          * @param {String} html
23048          */
23049         sync: true,
23050          /**
23051          * @event push
23052          * Fires when the iframe editor is updated with content from the textarea.
23053          * @param {Roo.HtmlEditorCore} this
23054          * @param {String} html
23055          */
23056         push: true,
23057         
23058         /**
23059          * @event editorevent
23060          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23061          * @param {Roo.HtmlEditorCore} this
23062          */
23063         editorevent: true
23064         
23065     });
23066     
23067     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23068     
23069     // defaults : white / black...
23070     this.applyBlacklists();
23071     
23072     
23073     
23074 };
23075
23076
23077 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23078
23079
23080      /**
23081      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23082      */
23083     
23084     owner : false,
23085     
23086      /**
23087      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23088      *                        Roo.resizable.
23089      */
23090     resizable : false,
23091      /**
23092      * @cfg {Number} height (in pixels)
23093      */   
23094     height: 300,
23095    /**
23096      * @cfg {Number} width (in pixels)
23097      */   
23098     width: 500,
23099     
23100     /**
23101      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23102      * 
23103      */
23104     stylesheets: false,
23105     
23106     // id of frame..
23107     frameId: false,
23108     
23109     // private properties
23110     validationEvent : false,
23111     deferHeight: true,
23112     initialized : false,
23113     activated : false,
23114     sourceEditMode : false,
23115     onFocus : Roo.emptyFn,
23116     iframePad:3,
23117     hideMode:'offsets',
23118     
23119     clearUp: true,
23120     
23121     // blacklist + whitelisted elements..
23122     black: false,
23123     white: false,
23124      
23125     bodyCls : '',
23126
23127     /**
23128      * Protected method that will not generally be called directly. It
23129      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23130      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23131      */
23132     getDocMarkup : function(){
23133         // body styles..
23134         var st = '';
23135         
23136         // inherit styels from page...?? 
23137         if (this.stylesheets === false) {
23138             
23139             Roo.get(document.head).select('style').each(function(node) {
23140                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23141             });
23142             
23143             Roo.get(document.head).select('link').each(function(node) { 
23144                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23145             });
23146             
23147         } else if (!this.stylesheets.length) {
23148                 // simple..
23149                 st = '<style type="text/css">' +
23150                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23151                    '</style>';
23152         } else { 
23153             st = '<style type="text/css">' +
23154                     this.stylesheets +
23155                 '</style>';
23156         }
23157         
23158         st +=  '<style type="text/css">' +
23159             'IMG { cursor: pointer } ' +
23160         '</style>';
23161
23162         var cls = 'roo-htmleditor-body';
23163         
23164         if(this.bodyCls.length){
23165             cls += ' ' + this.bodyCls;
23166         }
23167         
23168         return '<html><head>' + st  +
23169             //<style type="text/css">' +
23170             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23171             //'</style>' +
23172             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23173     },
23174
23175     // private
23176     onRender : function(ct, position)
23177     {
23178         var _t = this;
23179         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23180         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23181         
23182         
23183         this.el.dom.style.border = '0 none';
23184         this.el.dom.setAttribute('tabIndex', -1);
23185         this.el.addClass('x-hidden hide');
23186         
23187         
23188         
23189         if(Roo.isIE){ // fix IE 1px bogus margin
23190             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23191         }
23192        
23193         
23194         this.frameId = Roo.id();
23195         
23196          
23197         
23198         var iframe = this.owner.wrap.createChild({
23199             tag: 'iframe',
23200             cls: 'form-control', // bootstrap..
23201             id: this.frameId,
23202             name: this.frameId,
23203             frameBorder : 'no',
23204             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23205         }, this.el
23206         );
23207         
23208         
23209         this.iframe = iframe.dom;
23210
23211          this.assignDocWin();
23212         
23213         this.doc.designMode = 'on';
23214        
23215         this.doc.open();
23216         this.doc.write(this.getDocMarkup());
23217         this.doc.close();
23218
23219         
23220         var task = { // must defer to wait for browser to be ready
23221             run : function(){
23222                 //console.log("run task?" + this.doc.readyState);
23223                 this.assignDocWin();
23224                 if(this.doc.body || this.doc.readyState == 'complete'){
23225                     try {
23226                         this.doc.designMode="on";
23227                     } catch (e) {
23228                         return;
23229                     }
23230                     Roo.TaskMgr.stop(task);
23231                     this.initEditor.defer(10, this);
23232                 }
23233             },
23234             interval : 10,
23235             duration: 10000,
23236             scope: this
23237         };
23238         Roo.TaskMgr.start(task);
23239
23240     },
23241
23242     // private
23243     onResize : function(w, h)
23244     {
23245          Roo.log('resize: ' +w + ',' + h );
23246         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23247         if(!this.iframe){
23248             return;
23249         }
23250         if(typeof w == 'number'){
23251             
23252             this.iframe.style.width = w + 'px';
23253         }
23254         if(typeof h == 'number'){
23255             
23256             this.iframe.style.height = h + 'px';
23257             if(this.doc){
23258                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23259             }
23260         }
23261         
23262     },
23263
23264     /**
23265      * Toggles the editor between standard and source edit mode.
23266      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23267      */
23268     toggleSourceEdit : function(sourceEditMode){
23269         
23270         this.sourceEditMode = sourceEditMode === true;
23271         
23272         if(this.sourceEditMode){
23273  
23274             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23275             
23276         }else{
23277             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23278             //this.iframe.className = '';
23279             this.deferFocus();
23280         }
23281         //this.setSize(this.owner.wrap.getSize());
23282         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23283     },
23284
23285     
23286   
23287
23288     /**
23289      * Protected method that will not generally be called directly. If you need/want
23290      * custom HTML cleanup, this is the method you should override.
23291      * @param {String} html The HTML to be cleaned
23292      * return {String} The cleaned HTML
23293      */
23294     cleanHtml : function(html){
23295         html = String(html);
23296         if(html.length > 5){
23297             if(Roo.isSafari){ // strip safari nonsense
23298                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23299             }
23300         }
23301         if(html == '&nbsp;'){
23302             html = '';
23303         }
23304         return html;
23305     },
23306
23307     /**
23308      * HTML Editor -> Textarea
23309      * Protected method that will not generally be called directly. Syncs the contents
23310      * of the editor iframe with the textarea.
23311      */
23312     syncValue : function(){
23313         if(this.initialized){
23314             var bd = (this.doc.body || this.doc.documentElement);
23315             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23316             var html = bd.innerHTML;
23317             if(Roo.isSafari){
23318                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23319                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23320                 if(m && m[1]){
23321                     html = '<div style="'+m[0]+'">' + html + '</div>';
23322                 }
23323             }
23324             html = this.cleanHtml(html);
23325             // fix up the special chars.. normaly like back quotes in word...
23326             // however we do not want to do this with chinese..
23327             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23328                 
23329                 var cc = match.charCodeAt();
23330
23331                 // Get the character value, handling surrogate pairs
23332                 if (match.length == 2) {
23333                     // It's a surrogate pair, calculate the Unicode code point
23334                     var high = match.charCodeAt(0) - 0xD800;
23335                     var low  = match.charCodeAt(1) - 0xDC00;
23336                     cc = (high * 0x400) + low + 0x10000;
23337                 }  else if (
23338                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23339                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23340                     (cc >= 0xf900 && cc < 0xfb00 )
23341                 ) {
23342                         return match;
23343                 }  
23344          
23345                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23346                 return "&#" + cc + ";";
23347                 
23348                 
23349             });
23350             
23351             
23352              
23353             if(this.owner.fireEvent('beforesync', this, html) !== false){
23354                 this.el.dom.value = html;
23355                 this.owner.fireEvent('sync', this, html);
23356             }
23357         }
23358     },
23359
23360     /**
23361      * Protected method that will not generally be called directly. Pushes the value of the textarea
23362      * into the iframe editor.
23363      */
23364     pushValue : function(){
23365         if(this.initialized){
23366             var v = this.el.dom.value.trim();
23367             
23368 //            if(v.length < 1){
23369 //                v = '&#160;';
23370 //            }
23371             
23372             if(this.owner.fireEvent('beforepush', this, v) !== false){
23373                 var d = (this.doc.body || this.doc.documentElement);
23374                 d.innerHTML = v;
23375                 this.cleanUpPaste();
23376                 this.el.dom.value = d.innerHTML;
23377                 this.owner.fireEvent('push', this, v);
23378             }
23379         }
23380     },
23381
23382     // private
23383     deferFocus : function(){
23384         this.focus.defer(10, this);
23385     },
23386
23387     // doc'ed in Field
23388     focus : function(){
23389         if(this.win && !this.sourceEditMode){
23390             this.win.focus();
23391         }else{
23392             this.el.focus();
23393         }
23394     },
23395     
23396     assignDocWin: function()
23397     {
23398         var iframe = this.iframe;
23399         
23400          if(Roo.isIE){
23401             this.doc = iframe.contentWindow.document;
23402             this.win = iframe.contentWindow;
23403         } else {
23404 //            if (!Roo.get(this.frameId)) {
23405 //                return;
23406 //            }
23407 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23408 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23409             
23410             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23411                 return;
23412             }
23413             
23414             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23415             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23416         }
23417     },
23418     
23419     // private
23420     initEditor : function(){
23421         //console.log("INIT EDITOR");
23422         this.assignDocWin();
23423         
23424         
23425         
23426         this.doc.designMode="on";
23427         this.doc.open();
23428         this.doc.write(this.getDocMarkup());
23429         this.doc.close();
23430         
23431         var dbody = (this.doc.body || this.doc.documentElement);
23432         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23433         // this copies styles from the containing element into thsi one..
23434         // not sure why we need all of this..
23435         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23436         
23437         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23438         //ss['background-attachment'] = 'fixed'; // w3c
23439         dbody.bgProperties = 'fixed'; // ie
23440         //Roo.DomHelper.applyStyles(dbody, ss);
23441         Roo.EventManager.on(this.doc, {
23442             //'mousedown': this.onEditorEvent,
23443             'mouseup': this.onEditorEvent,
23444             'dblclick': this.onEditorEvent,
23445             'click': this.onEditorEvent,
23446             'keyup': this.onEditorEvent,
23447             buffer:100,
23448             scope: this
23449         });
23450         if(Roo.isGecko){
23451             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23452         }
23453         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23454             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23455         }
23456         this.initialized = true;
23457
23458         this.owner.fireEvent('initialize', this);
23459         this.pushValue();
23460     },
23461
23462     // private
23463     onDestroy : function(){
23464         
23465         
23466         
23467         if(this.rendered){
23468             
23469             //for (var i =0; i < this.toolbars.length;i++) {
23470             //    // fixme - ask toolbars for heights?
23471             //    this.toolbars[i].onDestroy();
23472            // }
23473             
23474             //this.wrap.dom.innerHTML = '';
23475             //this.wrap.remove();
23476         }
23477     },
23478
23479     // private
23480     onFirstFocus : function(){
23481         
23482         this.assignDocWin();
23483         
23484         
23485         this.activated = true;
23486          
23487     
23488         if(Roo.isGecko){ // prevent silly gecko errors
23489             this.win.focus();
23490             var s = this.win.getSelection();
23491             if(!s.focusNode || s.focusNode.nodeType != 3){
23492                 var r = s.getRangeAt(0);
23493                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23494                 r.collapse(true);
23495                 this.deferFocus();
23496             }
23497             try{
23498                 this.execCmd('useCSS', true);
23499                 this.execCmd('styleWithCSS', false);
23500             }catch(e){}
23501         }
23502         this.owner.fireEvent('activate', this);
23503     },
23504
23505     // private
23506     adjustFont: function(btn){
23507         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23508         //if(Roo.isSafari){ // safari
23509         //    adjust *= 2;
23510        // }
23511         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23512         if(Roo.isSafari){ // safari
23513             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23514             v =  (v < 10) ? 10 : v;
23515             v =  (v > 48) ? 48 : v;
23516             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23517             
23518         }
23519         
23520         
23521         v = Math.max(1, v+adjust);
23522         
23523         this.execCmd('FontSize', v  );
23524     },
23525
23526     onEditorEvent : function(e)
23527     {
23528         this.owner.fireEvent('editorevent', this, e);
23529       //  this.updateToolbar();
23530         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23531     },
23532
23533     insertTag : function(tg)
23534     {
23535         // could be a bit smarter... -> wrap the current selected tRoo..
23536         if (tg.toLowerCase() == 'span' ||
23537             tg.toLowerCase() == 'code' ||
23538             tg.toLowerCase() == 'sup' ||
23539             tg.toLowerCase() == 'sub' 
23540             ) {
23541             
23542             range = this.createRange(this.getSelection());
23543             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23544             wrappingNode.appendChild(range.extractContents());
23545             range.insertNode(wrappingNode);
23546
23547             return;
23548             
23549             
23550             
23551         }
23552         this.execCmd("formatblock",   tg);
23553         
23554     },
23555     
23556     insertText : function(txt)
23557     {
23558         
23559         
23560         var range = this.createRange();
23561         range.deleteContents();
23562                //alert(Sender.getAttribute('label'));
23563                
23564         range.insertNode(this.doc.createTextNode(txt));
23565     } ,
23566     
23567      
23568
23569     /**
23570      * Executes a Midas editor command on the editor document and performs necessary focus and
23571      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23572      * @param {String} cmd The Midas command
23573      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23574      */
23575     relayCmd : function(cmd, value){
23576         this.win.focus();
23577         this.execCmd(cmd, value);
23578         this.owner.fireEvent('editorevent', this);
23579         //this.updateToolbar();
23580         this.owner.deferFocus();
23581     },
23582
23583     /**
23584      * Executes a Midas editor command directly on the editor document.
23585      * For visual commands, you should use {@link #relayCmd} instead.
23586      * <b>This should only be called after the editor is initialized.</b>
23587      * @param {String} cmd The Midas command
23588      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23589      */
23590     execCmd : function(cmd, value){
23591         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23592         this.syncValue();
23593     },
23594  
23595  
23596    
23597     /**
23598      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23599      * to insert tRoo.
23600      * @param {String} text | dom node.. 
23601      */
23602     insertAtCursor : function(text)
23603     {
23604         
23605         if(!this.activated){
23606             return;
23607         }
23608         /*
23609         if(Roo.isIE){
23610             this.win.focus();
23611             var r = this.doc.selection.createRange();
23612             if(r){
23613                 r.collapse(true);
23614                 r.pasteHTML(text);
23615                 this.syncValue();
23616                 this.deferFocus();
23617             
23618             }
23619             return;
23620         }
23621         */
23622         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23623             this.win.focus();
23624             
23625             
23626             // from jquery ui (MIT licenced)
23627             var range, node;
23628             var win = this.win;
23629             
23630             if (win.getSelection && win.getSelection().getRangeAt) {
23631                 range = win.getSelection().getRangeAt(0);
23632                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23633                 range.insertNode(node);
23634             } else if (win.document.selection && win.document.selection.createRange) {
23635                 // no firefox support
23636                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23637                 win.document.selection.createRange().pasteHTML(txt);
23638             } else {
23639                 // no firefox support
23640                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23641                 this.execCmd('InsertHTML', txt);
23642             } 
23643             
23644             this.syncValue();
23645             
23646             this.deferFocus();
23647         }
23648     },
23649  // private
23650     mozKeyPress : function(e){
23651         if(e.ctrlKey){
23652             var c = e.getCharCode(), cmd;
23653           
23654             if(c > 0){
23655                 c = String.fromCharCode(c).toLowerCase();
23656                 switch(c){
23657                     case 'b':
23658                         cmd = 'bold';
23659                         break;
23660                     case 'i':
23661                         cmd = 'italic';
23662                         break;
23663                     
23664                     case 'u':
23665                         cmd = 'underline';
23666                         break;
23667                     
23668                     case 'v':
23669                         this.cleanUpPaste.defer(100, this);
23670                         return;
23671                         
23672                 }
23673                 if(cmd){
23674                     this.win.focus();
23675                     this.execCmd(cmd);
23676                     this.deferFocus();
23677                     e.preventDefault();
23678                 }
23679                 
23680             }
23681         }
23682     },
23683
23684     // private
23685     fixKeys : function(){ // load time branching for fastest keydown performance
23686         if(Roo.isIE){
23687             return function(e){
23688                 var k = e.getKey(), r;
23689                 if(k == e.TAB){
23690                     e.stopEvent();
23691                     r = this.doc.selection.createRange();
23692                     if(r){
23693                         r.collapse(true);
23694                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23695                         this.deferFocus();
23696                     }
23697                     return;
23698                 }
23699                 
23700                 if(k == e.ENTER){
23701                     r = this.doc.selection.createRange();
23702                     if(r){
23703                         var target = r.parentElement();
23704                         if(!target || target.tagName.toLowerCase() != 'li'){
23705                             e.stopEvent();
23706                             r.pasteHTML('<br />');
23707                             r.collapse(false);
23708                             r.select();
23709                         }
23710                     }
23711                 }
23712                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23713                     this.cleanUpPaste.defer(100, this);
23714                     return;
23715                 }
23716                 
23717                 
23718             };
23719         }else if(Roo.isOpera){
23720             return function(e){
23721                 var k = e.getKey();
23722                 if(k == e.TAB){
23723                     e.stopEvent();
23724                     this.win.focus();
23725                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23726                     this.deferFocus();
23727                 }
23728                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23729                     this.cleanUpPaste.defer(100, this);
23730                     return;
23731                 }
23732                 
23733             };
23734         }else if(Roo.isSafari){
23735             return function(e){
23736                 var k = e.getKey();
23737                 
23738                 if(k == e.TAB){
23739                     e.stopEvent();
23740                     this.execCmd('InsertText','\t');
23741                     this.deferFocus();
23742                     return;
23743                 }
23744                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23745                     this.cleanUpPaste.defer(100, this);
23746                     return;
23747                 }
23748                 
23749              };
23750         }
23751     }(),
23752     
23753     getAllAncestors: function()
23754     {
23755         var p = this.getSelectedNode();
23756         var a = [];
23757         if (!p) {
23758             a.push(p); // push blank onto stack..
23759             p = this.getParentElement();
23760         }
23761         
23762         
23763         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23764             a.push(p);
23765             p = p.parentNode;
23766         }
23767         a.push(this.doc.body);
23768         return a;
23769     },
23770     lastSel : false,
23771     lastSelNode : false,
23772     
23773     
23774     getSelection : function() 
23775     {
23776         this.assignDocWin();
23777         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23778     },
23779     
23780     getSelectedNode: function() 
23781     {
23782         // this may only work on Gecko!!!
23783         
23784         // should we cache this!!!!
23785         
23786         
23787         
23788          
23789         var range = this.createRange(this.getSelection()).cloneRange();
23790         
23791         if (Roo.isIE) {
23792             var parent = range.parentElement();
23793             while (true) {
23794                 var testRange = range.duplicate();
23795                 testRange.moveToElementText(parent);
23796                 if (testRange.inRange(range)) {
23797                     break;
23798                 }
23799                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23800                     break;
23801                 }
23802                 parent = parent.parentElement;
23803             }
23804             return parent;
23805         }
23806         
23807         // is ancestor a text element.
23808         var ac =  range.commonAncestorContainer;
23809         if (ac.nodeType == 3) {
23810             ac = ac.parentNode;
23811         }
23812         
23813         var ar = ac.childNodes;
23814          
23815         var nodes = [];
23816         var other_nodes = [];
23817         var has_other_nodes = false;
23818         for (var i=0;i<ar.length;i++) {
23819             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23820                 continue;
23821             }
23822             // fullly contained node.
23823             
23824             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23825                 nodes.push(ar[i]);
23826                 continue;
23827             }
23828             
23829             // probably selected..
23830             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23831                 other_nodes.push(ar[i]);
23832                 continue;
23833             }
23834             // outer..
23835             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23836                 continue;
23837             }
23838             
23839             
23840             has_other_nodes = true;
23841         }
23842         if (!nodes.length && other_nodes.length) {
23843             nodes= other_nodes;
23844         }
23845         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23846             return false;
23847         }
23848         
23849         return nodes[0];
23850     },
23851     createRange: function(sel)
23852     {
23853         // this has strange effects when using with 
23854         // top toolbar - not sure if it's a great idea.
23855         //this.editor.contentWindow.focus();
23856         if (typeof sel != "undefined") {
23857             try {
23858                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23859             } catch(e) {
23860                 return this.doc.createRange();
23861             }
23862         } else {
23863             return this.doc.createRange();
23864         }
23865     },
23866     getParentElement: function()
23867     {
23868         
23869         this.assignDocWin();
23870         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23871         
23872         var range = this.createRange(sel);
23873          
23874         try {
23875             var p = range.commonAncestorContainer;
23876             while (p.nodeType == 3) { // text node
23877                 p = p.parentNode;
23878             }
23879             return p;
23880         } catch (e) {
23881             return null;
23882         }
23883     
23884     },
23885     /***
23886      *
23887      * Range intersection.. the hard stuff...
23888      *  '-1' = before
23889      *  '0' = hits..
23890      *  '1' = after.
23891      *         [ -- selected range --- ]
23892      *   [fail]                        [fail]
23893      *
23894      *    basically..
23895      *      if end is before start or  hits it. fail.
23896      *      if start is after end or hits it fail.
23897      *
23898      *   if either hits (but other is outside. - then it's not 
23899      *   
23900      *    
23901      **/
23902     
23903     
23904     // @see http://www.thismuchiknow.co.uk/?p=64.
23905     rangeIntersectsNode : function(range, node)
23906     {
23907         var nodeRange = node.ownerDocument.createRange();
23908         try {
23909             nodeRange.selectNode(node);
23910         } catch (e) {
23911             nodeRange.selectNodeContents(node);
23912         }
23913     
23914         var rangeStartRange = range.cloneRange();
23915         rangeStartRange.collapse(true);
23916     
23917         var rangeEndRange = range.cloneRange();
23918         rangeEndRange.collapse(false);
23919     
23920         var nodeStartRange = nodeRange.cloneRange();
23921         nodeStartRange.collapse(true);
23922     
23923         var nodeEndRange = nodeRange.cloneRange();
23924         nodeEndRange.collapse(false);
23925     
23926         return rangeStartRange.compareBoundaryPoints(
23927                  Range.START_TO_START, nodeEndRange) == -1 &&
23928                rangeEndRange.compareBoundaryPoints(
23929                  Range.START_TO_START, nodeStartRange) == 1;
23930         
23931          
23932     },
23933     rangeCompareNode : function(range, node)
23934     {
23935         var nodeRange = node.ownerDocument.createRange();
23936         try {
23937             nodeRange.selectNode(node);
23938         } catch (e) {
23939             nodeRange.selectNodeContents(node);
23940         }
23941         
23942         
23943         range.collapse(true);
23944     
23945         nodeRange.collapse(true);
23946      
23947         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23948         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23949          
23950         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23951         
23952         var nodeIsBefore   =  ss == 1;
23953         var nodeIsAfter    = ee == -1;
23954         
23955         if (nodeIsBefore && nodeIsAfter) {
23956             return 0; // outer
23957         }
23958         if (!nodeIsBefore && nodeIsAfter) {
23959             return 1; //right trailed.
23960         }
23961         
23962         if (nodeIsBefore && !nodeIsAfter) {
23963             return 2;  // left trailed.
23964         }
23965         // fully contined.
23966         return 3;
23967     },
23968
23969     // private? - in a new class?
23970     cleanUpPaste :  function()
23971     {
23972         // cleans up the whole document..
23973         Roo.log('cleanuppaste');
23974         
23975         this.cleanUpChildren(this.doc.body);
23976         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23977         if (clean != this.doc.body.innerHTML) {
23978             this.doc.body.innerHTML = clean;
23979         }
23980         
23981     },
23982     
23983     cleanWordChars : function(input) {// change the chars to hex code
23984         var he = Roo.HtmlEditorCore;
23985         
23986         var output = input;
23987         Roo.each(he.swapCodes, function(sw) { 
23988             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23989             
23990             output = output.replace(swapper, sw[1]);
23991         });
23992         
23993         return output;
23994     },
23995     
23996     
23997     cleanUpChildren : function (n)
23998     {
23999         if (!n.childNodes.length) {
24000             return;
24001         }
24002         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24003            this.cleanUpChild(n.childNodes[i]);
24004         }
24005     },
24006     
24007     
24008         
24009     
24010     cleanUpChild : function (node)
24011     {
24012         var ed = this;
24013         //console.log(node);
24014         if (node.nodeName == "#text") {
24015             // clean up silly Windows -- stuff?
24016             return; 
24017         }
24018         if (node.nodeName == "#comment") {
24019             node.parentNode.removeChild(node);
24020             // clean up silly Windows -- stuff?
24021             return; 
24022         }
24023         var lcname = node.tagName.toLowerCase();
24024         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24025         // whitelist of tags..
24026         
24027         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24028             // remove node.
24029             node.parentNode.removeChild(node);
24030             return;
24031             
24032         }
24033         
24034         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24035         
24036         // spans with no attributes - just remove them..
24037         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24038             remove_keep_children = true;
24039         }
24040         
24041         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24042         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24043         
24044         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24045         //    remove_keep_children = true;
24046         //}
24047         
24048         if (remove_keep_children) {
24049             this.cleanUpChildren(node);
24050             // inserts everything just before this node...
24051             while (node.childNodes.length) {
24052                 var cn = node.childNodes[0];
24053                 node.removeChild(cn);
24054                 node.parentNode.insertBefore(cn, node);
24055             }
24056             node.parentNode.removeChild(node);
24057             return;
24058         }
24059         
24060         if (!node.attributes || !node.attributes.length) {
24061             
24062           
24063             
24064             
24065             this.cleanUpChildren(node);
24066             return;
24067         }
24068         
24069         function cleanAttr(n,v)
24070         {
24071             
24072             if (v.match(/^\./) || v.match(/^\//)) {
24073                 return;
24074             }
24075             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24076                 return;
24077             }
24078             if (v.match(/^#/)) {
24079                 return;
24080             }
24081 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24082             node.removeAttribute(n);
24083             
24084         }
24085         
24086         var cwhite = this.cwhite;
24087         var cblack = this.cblack;
24088             
24089         function cleanStyle(n,v)
24090         {
24091             if (v.match(/expression/)) { //XSS?? should we even bother..
24092                 node.removeAttribute(n);
24093                 return;
24094             }
24095             
24096             var parts = v.split(/;/);
24097             var clean = [];
24098             
24099             Roo.each(parts, function(p) {
24100                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24101                 if (!p.length) {
24102                     return true;
24103                 }
24104                 var l = p.split(':').shift().replace(/\s+/g,'');
24105                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24106                 
24107                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24108 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24109                     //node.removeAttribute(n);
24110                     return true;
24111                 }
24112                 //Roo.log()
24113                 // only allow 'c whitelisted system attributes'
24114                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24115 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24116                     //node.removeAttribute(n);
24117                     return true;
24118                 }
24119                 
24120                 
24121                  
24122                 
24123                 clean.push(p);
24124                 return true;
24125             });
24126             if (clean.length) { 
24127                 node.setAttribute(n, clean.join(';'));
24128             } else {
24129                 node.removeAttribute(n);
24130             }
24131             
24132         }
24133         
24134         
24135         for (var i = node.attributes.length-1; i > -1 ; i--) {
24136             var a = node.attributes[i];
24137             //console.log(a);
24138             
24139             if (a.name.toLowerCase().substr(0,2)=='on')  {
24140                 node.removeAttribute(a.name);
24141                 continue;
24142             }
24143             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24144                 node.removeAttribute(a.name);
24145                 continue;
24146             }
24147             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24148                 cleanAttr(a.name,a.value); // fixme..
24149                 continue;
24150             }
24151             if (a.name == 'style') {
24152                 cleanStyle(a.name,a.value);
24153                 continue;
24154             }
24155             /// clean up MS crap..
24156             // tecnically this should be a list of valid class'es..
24157             
24158             
24159             if (a.name == 'class') {
24160                 if (a.value.match(/^Mso/)) {
24161                     node.removeAttribute('class');
24162                 }
24163                 
24164                 if (a.value.match(/^body$/)) {
24165                     node.removeAttribute('class');
24166                 }
24167                 continue;
24168             }
24169             
24170             // style cleanup!?
24171             // class cleanup?
24172             
24173         }
24174         
24175         
24176         this.cleanUpChildren(node);
24177         
24178         
24179     },
24180     
24181     /**
24182      * Clean up MS wordisms...
24183      */
24184     cleanWord : function(node)
24185     {
24186         if (!node) {
24187             this.cleanWord(this.doc.body);
24188             return;
24189         }
24190         
24191         if(
24192                 node.nodeName == 'SPAN' &&
24193                 !node.hasAttributes() &&
24194                 node.childNodes.length == 1 &&
24195                 node.firstChild.nodeName == "#text"  
24196         ) {
24197             var textNode = node.firstChild;
24198             node.removeChild(textNode);
24199             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24200                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24201             }
24202             node.parentNode.insertBefore(textNode, node);
24203             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24204                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24205             }
24206             node.parentNode.removeChild(node);
24207         }
24208         
24209         if (node.nodeName == "#text") {
24210             // clean up silly Windows -- stuff?
24211             return; 
24212         }
24213         if (node.nodeName == "#comment") {
24214             node.parentNode.removeChild(node);
24215             // clean up silly Windows -- stuff?
24216             return; 
24217         }
24218         
24219         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24220             node.parentNode.removeChild(node);
24221             return;
24222         }
24223         //Roo.log(node.tagName);
24224         // remove - but keep children..
24225         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24226             //Roo.log('-- removed');
24227             while (node.childNodes.length) {
24228                 var cn = node.childNodes[0];
24229                 node.removeChild(cn);
24230                 node.parentNode.insertBefore(cn, node);
24231                 // move node to parent - and clean it..
24232                 this.cleanWord(cn);
24233             }
24234             node.parentNode.removeChild(node);
24235             /// no need to iterate chidlren = it's got none..
24236             //this.iterateChildren(node, this.cleanWord);
24237             return;
24238         }
24239         // clean styles
24240         if (node.className.length) {
24241             
24242             var cn = node.className.split(/\W+/);
24243             var cna = [];
24244             Roo.each(cn, function(cls) {
24245                 if (cls.match(/Mso[a-zA-Z]+/)) {
24246                     return;
24247                 }
24248                 cna.push(cls);
24249             });
24250             node.className = cna.length ? cna.join(' ') : '';
24251             if (!cna.length) {
24252                 node.removeAttribute("class");
24253             }
24254         }
24255         
24256         if (node.hasAttribute("lang")) {
24257             node.removeAttribute("lang");
24258         }
24259         
24260         if (node.hasAttribute("style")) {
24261             
24262             var styles = node.getAttribute("style").split(";");
24263             var nstyle = [];
24264             Roo.each(styles, function(s) {
24265                 if (!s.match(/:/)) {
24266                     return;
24267                 }
24268                 var kv = s.split(":");
24269                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24270                     return;
24271                 }
24272                 // what ever is left... we allow.
24273                 nstyle.push(s);
24274             });
24275             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24276             if (!nstyle.length) {
24277                 node.removeAttribute('style');
24278             }
24279         }
24280         this.iterateChildren(node, this.cleanWord);
24281         
24282         
24283         
24284     },
24285     /**
24286      * iterateChildren of a Node, calling fn each time, using this as the scole..
24287      * @param {DomNode} node node to iterate children of.
24288      * @param {Function} fn method of this class to call on each item.
24289      */
24290     iterateChildren : function(node, fn)
24291     {
24292         if (!node.childNodes.length) {
24293                 return;
24294         }
24295         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24296            fn.call(this, node.childNodes[i])
24297         }
24298     },
24299     
24300     
24301     /**
24302      * cleanTableWidths.
24303      *
24304      * Quite often pasting from word etc.. results in tables with column and widths.
24305      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24306      *
24307      */
24308     cleanTableWidths : function(node)
24309     {
24310          
24311          
24312         if (!node) {
24313             this.cleanTableWidths(this.doc.body);
24314             return;
24315         }
24316         
24317         // ignore list...
24318         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24319             return; 
24320         }
24321         Roo.log(node.tagName);
24322         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24323             this.iterateChildren(node, this.cleanTableWidths);
24324             return;
24325         }
24326         if (node.hasAttribute('width')) {
24327             node.removeAttribute('width');
24328         }
24329         
24330          
24331         if (node.hasAttribute("style")) {
24332             // pretty basic...
24333             
24334             var styles = node.getAttribute("style").split(";");
24335             var nstyle = [];
24336             Roo.each(styles, function(s) {
24337                 if (!s.match(/:/)) {
24338                     return;
24339                 }
24340                 var kv = s.split(":");
24341                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24342                     return;
24343                 }
24344                 // what ever is left... we allow.
24345                 nstyle.push(s);
24346             });
24347             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24348             if (!nstyle.length) {
24349                 node.removeAttribute('style');
24350             }
24351         }
24352         
24353         this.iterateChildren(node, this.cleanTableWidths);
24354         
24355         
24356     },
24357     
24358     
24359     
24360     
24361     domToHTML : function(currentElement, depth, nopadtext) {
24362         
24363         depth = depth || 0;
24364         nopadtext = nopadtext || false;
24365     
24366         if (!currentElement) {
24367             return this.domToHTML(this.doc.body);
24368         }
24369         
24370         //Roo.log(currentElement);
24371         var j;
24372         var allText = false;
24373         var nodeName = currentElement.nodeName;
24374         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24375         
24376         if  (nodeName == '#text') {
24377             
24378             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24379         }
24380         
24381         
24382         var ret = '';
24383         if (nodeName != 'BODY') {
24384              
24385             var i = 0;
24386             // Prints the node tagName, such as <A>, <IMG>, etc
24387             if (tagName) {
24388                 var attr = [];
24389                 for(i = 0; i < currentElement.attributes.length;i++) {
24390                     // quoting?
24391                     var aname = currentElement.attributes.item(i).name;
24392                     if (!currentElement.attributes.item(i).value.length) {
24393                         continue;
24394                     }
24395                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24396                 }
24397                 
24398                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24399             } 
24400             else {
24401                 
24402                 // eack
24403             }
24404         } else {
24405             tagName = false;
24406         }
24407         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24408             return ret;
24409         }
24410         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24411             nopadtext = true;
24412         }
24413         
24414         
24415         // Traverse the tree
24416         i = 0;
24417         var currentElementChild = currentElement.childNodes.item(i);
24418         var allText = true;
24419         var innerHTML  = '';
24420         lastnode = '';
24421         while (currentElementChild) {
24422             // Formatting code (indent the tree so it looks nice on the screen)
24423             var nopad = nopadtext;
24424             if (lastnode == 'SPAN') {
24425                 nopad  = true;
24426             }
24427             // text
24428             if  (currentElementChild.nodeName == '#text') {
24429                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24430                 toadd = nopadtext ? toadd : toadd.trim();
24431                 if (!nopad && toadd.length > 80) {
24432                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24433                 }
24434                 innerHTML  += toadd;
24435                 
24436                 i++;
24437                 currentElementChild = currentElement.childNodes.item(i);
24438                 lastNode = '';
24439                 continue;
24440             }
24441             allText = false;
24442             
24443             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24444                 
24445             // Recursively traverse the tree structure of the child node
24446             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24447             lastnode = currentElementChild.nodeName;
24448             i++;
24449             currentElementChild=currentElement.childNodes.item(i);
24450         }
24451         
24452         ret += innerHTML;
24453         
24454         if (!allText) {
24455                 // The remaining code is mostly for formatting the tree
24456             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24457         }
24458         
24459         
24460         if (tagName) {
24461             ret+= "</"+tagName+">";
24462         }
24463         return ret;
24464         
24465     },
24466         
24467     applyBlacklists : function()
24468     {
24469         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24470         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24471         
24472         this.white = [];
24473         this.black = [];
24474         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24475             if (b.indexOf(tag) > -1) {
24476                 return;
24477             }
24478             this.white.push(tag);
24479             
24480         }, this);
24481         
24482         Roo.each(w, function(tag) {
24483             if (b.indexOf(tag) > -1) {
24484                 return;
24485             }
24486             if (this.white.indexOf(tag) > -1) {
24487                 return;
24488             }
24489             this.white.push(tag);
24490             
24491         }, this);
24492         
24493         
24494         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24495             if (w.indexOf(tag) > -1) {
24496                 return;
24497             }
24498             this.black.push(tag);
24499             
24500         }, this);
24501         
24502         Roo.each(b, function(tag) {
24503             if (w.indexOf(tag) > -1) {
24504                 return;
24505             }
24506             if (this.black.indexOf(tag) > -1) {
24507                 return;
24508             }
24509             this.black.push(tag);
24510             
24511         }, this);
24512         
24513         
24514         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24515         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24516         
24517         this.cwhite = [];
24518         this.cblack = [];
24519         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24520             if (b.indexOf(tag) > -1) {
24521                 return;
24522             }
24523             this.cwhite.push(tag);
24524             
24525         }, this);
24526         
24527         Roo.each(w, function(tag) {
24528             if (b.indexOf(tag) > -1) {
24529                 return;
24530             }
24531             if (this.cwhite.indexOf(tag) > -1) {
24532                 return;
24533             }
24534             this.cwhite.push(tag);
24535             
24536         }, this);
24537         
24538         
24539         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24540             if (w.indexOf(tag) > -1) {
24541                 return;
24542             }
24543             this.cblack.push(tag);
24544             
24545         }, this);
24546         
24547         Roo.each(b, function(tag) {
24548             if (w.indexOf(tag) > -1) {
24549                 return;
24550             }
24551             if (this.cblack.indexOf(tag) > -1) {
24552                 return;
24553             }
24554             this.cblack.push(tag);
24555             
24556         }, this);
24557     },
24558     
24559     setStylesheets : function(stylesheets)
24560     {
24561         if(typeof(stylesheets) == 'string'){
24562             Roo.get(this.iframe.contentDocument.head).createChild({
24563                 tag : 'link',
24564                 rel : 'stylesheet',
24565                 type : 'text/css',
24566                 href : stylesheets
24567             });
24568             
24569             return;
24570         }
24571         var _this = this;
24572      
24573         Roo.each(stylesheets, function(s) {
24574             if(!s.length){
24575                 return;
24576             }
24577             
24578             Roo.get(_this.iframe.contentDocument.head).createChild({
24579                 tag : 'link',
24580                 rel : 'stylesheet',
24581                 type : 'text/css',
24582                 href : s
24583             });
24584         });
24585
24586         
24587     },
24588     
24589     removeStylesheets : function()
24590     {
24591         var _this = this;
24592         
24593         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24594             s.remove();
24595         });
24596     },
24597     
24598     setStyle : function(style)
24599     {
24600         Roo.get(this.iframe.contentDocument.head).createChild({
24601             tag : 'style',
24602             type : 'text/css',
24603             html : style
24604         });
24605
24606         return;
24607     }
24608     
24609     // hide stuff that is not compatible
24610     /**
24611      * @event blur
24612      * @hide
24613      */
24614     /**
24615      * @event change
24616      * @hide
24617      */
24618     /**
24619      * @event focus
24620      * @hide
24621      */
24622     /**
24623      * @event specialkey
24624      * @hide
24625      */
24626     /**
24627      * @cfg {String} fieldClass @hide
24628      */
24629     /**
24630      * @cfg {String} focusClass @hide
24631      */
24632     /**
24633      * @cfg {String} autoCreate @hide
24634      */
24635     /**
24636      * @cfg {String} inputType @hide
24637      */
24638     /**
24639      * @cfg {String} invalidClass @hide
24640      */
24641     /**
24642      * @cfg {String} invalidText @hide
24643      */
24644     /**
24645      * @cfg {String} msgFx @hide
24646      */
24647     /**
24648      * @cfg {String} validateOnBlur @hide
24649      */
24650 });
24651
24652 Roo.HtmlEditorCore.white = [
24653         'area', 'br', 'img', 'input', 'hr', 'wbr',
24654         
24655        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24656        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24657        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24658        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24659        'table',   'ul',         'xmp', 
24660        
24661        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24662       'thead',   'tr', 
24663      
24664       'dir', 'menu', 'ol', 'ul', 'dl',
24665        
24666       'embed',  'object'
24667 ];
24668
24669
24670 Roo.HtmlEditorCore.black = [
24671     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24672         'applet', // 
24673         'base',   'basefont', 'bgsound', 'blink',  'body', 
24674         'frame',  'frameset', 'head',    'html',   'ilayer', 
24675         'iframe', 'layer',  'link',     'meta',    'object',   
24676         'script', 'style' ,'title',  'xml' // clean later..
24677 ];
24678 Roo.HtmlEditorCore.clean = [
24679     'script', 'style', 'title', 'xml'
24680 ];
24681 Roo.HtmlEditorCore.remove = [
24682     'font'
24683 ];
24684 // attributes..
24685
24686 Roo.HtmlEditorCore.ablack = [
24687     'on'
24688 ];
24689     
24690 Roo.HtmlEditorCore.aclean = [ 
24691     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24692 ];
24693
24694 // protocols..
24695 Roo.HtmlEditorCore.pwhite= [
24696         'http',  'https',  'mailto'
24697 ];
24698
24699 // white listed style attributes.
24700 Roo.HtmlEditorCore.cwhite= [
24701       //  'text-align', /// default is to allow most things..
24702       
24703          
24704 //        'font-size'//??
24705 ];
24706
24707 // black listed style attributes.
24708 Roo.HtmlEditorCore.cblack= [
24709       //  'font-size' -- this can be set by the project 
24710 ];
24711
24712
24713 Roo.HtmlEditorCore.swapCodes   =[ 
24714     [    8211, "--" ], 
24715     [    8212, "--" ], 
24716     [    8216,  "'" ],  
24717     [    8217, "'" ],  
24718     [    8220, '"' ],  
24719     [    8221, '"' ],  
24720     [    8226, "*" ],  
24721     [    8230, "..." ]
24722 ]; 
24723
24724     /*
24725  * - LGPL
24726  *
24727  * HtmlEditor
24728  * 
24729  */
24730
24731 /**
24732  * @class Roo.bootstrap.HtmlEditor
24733  * @extends Roo.bootstrap.TextArea
24734  * Bootstrap HtmlEditor class
24735
24736  * @constructor
24737  * Create a new HtmlEditor
24738  * @param {Object} config The config object
24739  */
24740
24741 Roo.bootstrap.HtmlEditor = function(config){
24742     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24743     if (!this.toolbars) {
24744         this.toolbars = [];
24745     }
24746     
24747     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24748     this.addEvents({
24749             /**
24750              * @event initialize
24751              * Fires when the editor is fully initialized (including the iframe)
24752              * @param {HtmlEditor} this
24753              */
24754             initialize: true,
24755             /**
24756              * @event activate
24757              * Fires when the editor is first receives the focus. Any insertion must wait
24758              * until after this event.
24759              * @param {HtmlEditor} this
24760              */
24761             activate: true,
24762              /**
24763              * @event beforesync
24764              * Fires before the textarea is updated with content from the editor iframe. Return false
24765              * to cancel the sync.
24766              * @param {HtmlEditor} this
24767              * @param {String} html
24768              */
24769             beforesync: true,
24770              /**
24771              * @event beforepush
24772              * Fires before the iframe editor is updated with content from the textarea. Return false
24773              * to cancel the push.
24774              * @param {HtmlEditor} this
24775              * @param {String} html
24776              */
24777             beforepush: true,
24778              /**
24779              * @event sync
24780              * Fires when the textarea is updated with content from the editor iframe.
24781              * @param {HtmlEditor} this
24782              * @param {String} html
24783              */
24784             sync: true,
24785              /**
24786              * @event push
24787              * Fires when the iframe editor is updated with content from the textarea.
24788              * @param {HtmlEditor} this
24789              * @param {String} html
24790              */
24791             push: true,
24792              /**
24793              * @event editmodechange
24794              * Fires when the editor switches edit modes
24795              * @param {HtmlEditor} this
24796              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24797              */
24798             editmodechange: true,
24799             /**
24800              * @event editorevent
24801              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24802              * @param {HtmlEditor} this
24803              */
24804             editorevent: true,
24805             /**
24806              * @event firstfocus
24807              * Fires when on first focus - needed by toolbars..
24808              * @param {HtmlEditor} this
24809              */
24810             firstfocus: true,
24811             /**
24812              * @event autosave
24813              * Auto save the htmlEditor value as a file into Events
24814              * @param {HtmlEditor} this
24815              */
24816             autosave: true,
24817             /**
24818              * @event savedpreview
24819              * preview the saved version of htmlEditor
24820              * @param {HtmlEditor} this
24821              */
24822             savedpreview: true
24823         });
24824 };
24825
24826
24827 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24828     
24829     
24830       /**
24831      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24832      */
24833     toolbars : false,
24834     
24835      /**
24836     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24837     */
24838     btns : [],
24839    
24840      /**
24841      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24842      *                        Roo.resizable.
24843      */
24844     resizable : false,
24845      /**
24846      * @cfg {Number} height (in pixels)
24847      */   
24848     height: 300,
24849    /**
24850      * @cfg {Number} width (in pixels)
24851      */   
24852     width: false,
24853     
24854     /**
24855      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24856      * 
24857      */
24858     stylesheets: false,
24859     
24860     // id of frame..
24861     frameId: false,
24862     
24863     // private properties
24864     validationEvent : false,
24865     deferHeight: true,
24866     initialized : false,
24867     activated : false,
24868     
24869     onFocus : Roo.emptyFn,
24870     iframePad:3,
24871     hideMode:'offsets',
24872     
24873     tbContainer : false,
24874     
24875     bodyCls : '',
24876     
24877     toolbarContainer :function() {
24878         return this.wrap.select('.x-html-editor-tb',true).first();
24879     },
24880
24881     /**
24882      * Protected method that will not generally be called directly. It
24883      * is called when the editor creates its toolbar. Override this method if you need to
24884      * add custom toolbar buttons.
24885      * @param {HtmlEditor} editor
24886      */
24887     createToolbar : function(){
24888         Roo.log('renewing');
24889         Roo.log("create toolbars");
24890         
24891         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24892         this.toolbars[0].render(this.toolbarContainer());
24893         
24894         return;
24895         
24896 //        if (!editor.toolbars || !editor.toolbars.length) {
24897 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24898 //        }
24899 //        
24900 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24901 //            editor.toolbars[i] = Roo.factory(
24902 //                    typeof(editor.toolbars[i]) == 'string' ?
24903 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24904 //                Roo.bootstrap.HtmlEditor);
24905 //            editor.toolbars[i].init(editor);
24906 //        }
24907     },
24908
24909      
24910     // private
24911     onRender : function(ct, position)
24912     {
24913        // Roo.log("Call onRender: " + this.xtype);
24914         var _t = this;
24915         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24916       
24917         this.wrap = this.inputEl().wrap({
24918             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24919         });
24920         
24921         this.editorcore.onRender(ct, position);
24922          
24923         if (this.resizable) {
24924             this.resizeEl = new Roo.Resizable(this.wrap, {
24925                 pinned : true,
24926                 wrap: true,
24927                 dynamic : true,
24928                 minHeight : this.height,
24929                 height: this.height,
24930                 handles : this.resizable,
24931                 width: this.width,
24932                 listeners : {
24933                     resize : function(r, w, h) {
24934                         _t.onResize(w,h); // -something
24935                     }
24936                 }
24937             });
24938             
24939         }
24940         this.createToolbar(this);
24941        
24942         
24943         if(!this.width && this.resizable){
24944             this.setSize(this.wrap.getSize());
24945         }
24946         if (this.resizeEl) {
24947             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24948             // should trigger onReize..
24949         }
24950         
24951     },
24952
24953     // private
24954     onResize : function(w, h)
24955     {
24956         Roo.log('resize: ' +w + ',' + h );
24957         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24958         var ew = false;
24959         var eh = false;
24960         
24961         if(this.inputEl() ){
24962             if(typeof w == 'number'){
24963                 var aw = w - this.wrap.getFrameWidth('lr');
24964                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24965                 ew = aw;
24966             }
24967             if(typeof h == 'number'){
24968                  var tbh = -11;  // fixme it needs to tool bar size!
24969                 for (var i =0; i < this.toolbars.length;i++) {
24970                     // fixme - ask toolbars for heights?
24971                     tbh += this.toolbars[i].el.getHeight();
24972                     //if (this.toolbars[i].footer) {
24973                     //    tbh += this.toolbars[i].footer.el.getHeight();
24974                     //}
24975                 }
24976               
24977                 
24978                 
24979                 
24980                 
24981                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24982                 ah -= 5; // knock a few pixes off for look..
24983                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24984                 var eh = ah;
24985             }
24986         }
24987         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24988         this.editorcore.onResize(ew,eh);
24989         
24990     },
24991
24992     /**
24993      * Toggles the editor between standard and source edit mode.
24994      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24995      */
24996     toggleSourceEdit : function(sourceEditMode)
24997     {
24998         this.editorcore.toggleSourceEdit(sourceEditMode);
24999         
25000         if(this.editorcore.sourceEditMode){
25001             Roo.log('editor - showing textarea');
25002             
25003 //            Roo.log('in');
25004 //            Roo.log(this.syncValue());
25005             this.syncValue();
25006             this.inputEl().removeClass(['hide', 'x-hidden']);
25007             this.inputEl().dom.removeAttribute('tabIndex');
25008             this.inputEl().focus();
25009         }else{
25010             Roo.log('editor - hiding textarea');
25011 //            Roo.log('out')
25012 //            Roo.log(this.pushValue()); 
25013             this.pushValue();
25014             
25015             this.inputEl().addClass(['hide', 'x-hidden']);
25016             this.inputEl().dom.setAttribute('tabIndex', -1);
25017             //this.deferFocus();
25018         }
25019          
25020         if(this.resizable){
25021             this.setSize(this.wrap.getSize());
25022         }
25023         
25024         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25025     },
25026  
25027     // private (for BoxComponent)
25028     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25029
25030     // private (for BoxComponent)
25031     getResizeEl : function(){
25032         return this.wrap;
25033     },
25034
25035     // private (for BoxComponent)
25036     getPositionEl : function(){
25037         return this.wrap;
25038     },
25039
25040     // private
25041     initEvents : function(){
25042         this.originalValue = this.getValue();
25043     },
25044
25045 //    /**
25046 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25047 //     * @method
25048 //     */
25049 //    markInvalid : Roo.emptyFn,
25050 //    /**
25051 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25052 //     * @method
25053 //     */
25054 //    clearInvalid : Roo.emptyFn,
25055
25056     setValue : function(v){
25057         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25058         this.editorcore.pushValue();
25059     },
25060
25061      
25062     // private
25063     deferFocus : function(){
25064         this.focus.defer(10, this);
25065     },
25066
25067     // doc'ed in Field
25068     focus : function(){
25069         this.editorcore.focus();
25070         
25071     },
25072       
25073
25074     // private
25075     onDestroy : function(){
25076         
25077         
25078         
25079         if(this.rendered){
25080             
25081             for (var i =0; i < this.toolbars.length;i++) {
25082                 // fixme - ask toolbars for heights?
25083                 this.toolbars[i].onDestroy();
25084             }
25085             
25086             this.wrap.dom.innerHTML = '';
25087             this.wrap.remove();
25088         }
25089     },
25090
25091     // private
25092     onFirstFocus : function(){
25093         //Roo.log("onFirstFocus");
25094         this.editorcore.onFirstFocus();
25095          for (var i =0; i < this.toolbars.length;i++) {
25096             this.toolbars[i].onFirstFocus();
25097         }
25098         
25099     },
25100     
25101     // private
25102     syncValue : function()
25103     {   
25104         this.editorcore.syncValue();
25105     },
25106     
25107     pushValue : function()
25108     {   
25109         this.editorcore.pushValue();
25110     }
25111      
25112     
25113     // hide stuff that is not compatible
25114     /**
25115      * @event blur
25116      * @hide
25117      */
25118     /**
25119      * @event change
25120      * @hide
25121      */
25122     /**
25123      * @event focus
25124      * @hide
25125      */
25126     /**
25127      * @event specialkey
25128      * @hide
25129      */
25130     /**
25131      * @cfg {String} fieldClass @hide
25132      */
25133     /**
25134      * @cfg {String} focusClass @hide
25135      */
25136     /**
25137      * @cfg {String} autoCreate @hide
25138      */
25139     /**
25140      * @cfg {String} inputType @hide
25141      */
25142      
25143     /**
25144      * @cfg {String} invalidText @hide
25145      */
25146     /**
25147      * @cfg {String} msgFx @hide
25148      */
25149     /**
25150      * @cfg {String} validateOnBlur @hide
25151      */
25152 });
25153  
25154     
25155    
25156    
25157    
25158       
25159 Roo.namespace('Roo.bootstrap.htmleditor');
25160 /**
25161  * @class Roo.bootstrap.HtmlEditorToolbar1
25162  * Basic Toolbar
25163  * 
25164  * @example
25165  * Usage:
25166  *
25167  new Roo.bootstrap.HtmlEditor({
25168     ....
25169     toolbars : [
25170         new Roo.bootstrap.HtmlEditorToolbar1({
25171             disable : { fonts: 1 , format: 1, ..., ... , ...],
25172             btns : [ .... ]
25173         })
25174     }
25175      
25176  * 
25177  * @cfg {Object} disable List of elements to disable..
25178  * @cfg {Array} btns List of additional buttons.
25179  * 
25180  * 
25181  * NEEDS Extra CSS? 
25182  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25183  */
25184  
25185 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25186 {
25187     
25188     Roo.apply(this, config);
25189     
25190     // default disabled, based on 'good practice'..
25191     this.disable = this.disable || {};
25192     Roo.applyIf(this.disable, {
25193         fontSize : true,
25194         colors : true,
25195         specialElements : true
25196     });
25197     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25198     
25199     this.editor = config.editor;
25200     this.editorcore = config.editor.editorcore;
25201     
25202     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25203     
25204     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25205     // dont call parent... till later.
25206 }
25207 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25208      
25209     bar : true,
25210     
25211     editor : false,
25212     editorcore : false,
25213     
25214     
25215     formats : [
25216         "p" ,  
25217         "h1","h2","h3","h4","h5","h6", 
25218         "pre", "code", 
25219         "abbr", "acronym", "address", "cite", "samp", "var",
25220         'div','span'
25221     ],
25222     
25223     onRender : function(ct, position)
25224     {
25225        // Roo.log("Call onRender: " + this.xtype);
25226         
25227        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25228        Roo.log(this.el);
25229        this.el.dom.style.marginBottom = '0';
25230        var _this = this;
25231        var editorcore = this.editorcore;
25232        var editor= this.editor;
25233        
25234        var children = [];
25235        var btn = function(id,cmd , toggle, handler, html){
25236        
25237             var  event = toggle ? 'toggle' : 'click';
25238        
25239             var a = {
25240                 size : 'sm',
25241                 xtype: 'Button',
25242                 xns: Roo.bootstrap,
25243                 //glyphicon : id,
25244                 fa: id,
25245                 cmd : id || cmd,
25246                 enableToggle:toggle !== false,
25247                 html : html || '',
25248                 pressed : toggle ? false : null,
25249                 listeners : {}
25250             };
25251             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25252                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25253             };
25254             children.push(a);
25255             return a;
25256        }
25257        
25258     //    var cb_box = function...
25259         
25260         var style = {
25261                 xtype: 'Button',
25262                 size : 'sm',
25263                 xns: Roo.bootstrap,
25264                 fa : 'font',
25265                 //html : 'submit'
25266                 menu : {
25267                     xtype: 'Menu',
25268                     xns: Roo.bootstrap,
25269                     items:  []
25270                 }
25271         };
25272         Roo.each(this.formats, function(f) {
25273             style.menu.items.push({
25274                 xtype :'MenuItem',
25275                 xns: Roo.bootstrap,
25276                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25277                 tagname : f,
25278                 listeners : {
25279                     click : function()
25280                     {
25281                         editorcore.insertTag(this.tagname);
25282                         editor.focus();
25283                     }
25284                 }
25285                 
25286             });
25287         });
25288         children.push(style);   
25289         
25290         btn('bold',false,true);
25291         btn('italic',false,true);
25292         btn('align-left', 'justifyleft',true);
25293         btn('align-center', 'justifycenter',true);
25294         btn('align-right' , 'justifyright',true);
25295         btn('link', false, false, function(btn) {
25296             //Roo.log("create link?");
25297             var url = prompt(this.createLinkText, this.defaultLinkValue);
25298             if(url && url != 'http:/'+'/'){
25299                 this.editorcore.relayCmd('createlink', url);
25300             }
25301         }),
25302         btn('list','insertunorderedlist',true);
25303         btn('pencil', false,true, function(btn){
25304                 Roo.log(this);
25305                 this.toggleSourceEdit(btn.pressed);
25306         });
25307         
25308         if (this.editor.btns.length > 0) {
25309             for (var i = 0; i<this.editor.btns.length; i++) {
25310                 children.push(this.editor.btns[i]);
25311             }
25312         }
25313         
25314         /*
25315         var cog = {
25316                 xtype: 'Button',
25317                 size : 'sm',
25318                 xns: Roo.bootstrap,
25319                 glyphicon : 'cog',
25320                 //html : 'submit'
25321                 menu : {
25322                     xtype: 'Menu',
25323                     xns: Roo.bootstrap,
25324                     items:  []
25325                 }
25326         };
25327         
25328         cog.menu.items.push({
25329             xtype :'MenuItem',
25330             xns: Roo.bootstrap,
25331             html : Clean styles,
25332             tagname : f,
25333             listeners : {
25334                 click : function()
25335                 {
25336                     editorcore.insertTag(this.tagname);
25337                     editor.focus();
25338                 }
25339             }
25340             
25341         });
25342        */
25343         
25344          
25345        this.xtype = 'NavSimplebar';
25346         
25347         for(var i=0;i< children.length;i++) {
25348             
25349             this.buttons.add(this.addxtypeChild(children[i]));
25350             
25351         }
25352         
25353         editor.on('editorevent', this.updateToolbar, this);
25354     },
25355     onBtnClick : function(id)
25356     {
25357        this.editorcore.relayCmd(id);
25358        this.editorcore.focus();
25359     },
25360     
25361     /**
25362      * Protected method that will not generally be called directly. It triggers
25363      * a toolbar update by reading the markup state of the current selection in the editor.
25364      */
25365     updateToolbar: function(){
25366
25367         if(!this.editorcore.activated){
25368             this.editor.onFirstFocus(); // is this neeed?
25369             return;
25370         }
25371
25372         var btns = this.buttons; 
25373         var doc = this.editorcore.doc;
25374         btns.get('bold').setActive(doc.queryCommandState('bold'));
25375         btns.get('italic').setActive(doc.queryCommandState('italic'));
25376         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25377         
25378         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25379         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25380         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25381         
25382         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25383         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25384          /*
25385         
25386         var ans = this.editorcore.getAllAncestors();
25387         if (this.formatCombo) {
25388             
25389             
25390             var store = this.formatCombo.store;
25391             this.formatCombo.setValue("");
25392             for (var i =0; i < ans.length;i++) {
25393                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25394                     // select it..
25395                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25396                     break;
25397                 }
25398             }
25399         }
25400         
25401         
25402         
25403         // hides menus... - so this cant be on a menu...
25404         Roo.bootstrap.MenuMgr.hideAll();
25405         */
25406         Roo.bootstrap.MenuMgr.hideAll();
25407         //this.editorsyncValue();
25408     },
25409     onFirstFocus: function() {
25410         this.buttons.each(function(item){
25411            item.enable();
25412         });
25413     },
25414     toggleSourceEdit : function(sourceEditMode){
25415         
25416           
25417         if(sourceEditMode){
25418             Roo.log("disabling buttons");
25419            this.buttons.each( function(item){
25420                 if(item.cmd != 'pencil'){
25421                     item.disable();
25422                 }
25423             });
25424           
25425         }else{
25426             Roo.log("enabling buttons");
25427             if(this.editorcore.initialized){
25428                 this.buttons.each( function(item){
25429                     item.enable();
25430                 });
25431             }
25432             
25433         }
25434         Roo.log("calling toggole on editor");
25435         // tell the editor that it's been pressed..
25436         this.editor.toggleSourceEdit(sourceEditMode);
25437        
25438     }
25439 });
25440
25441
25442
25443
25444
25445 /**
25446  * @class Roo.bootstrap.Table.AbstractSelectionModel
25447  * @extends Roo.util.Observable
25448  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25449  * implemented by descendant classes.  This class should not be directly instantiated.
25450  * @constructor
25451  */
25452 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25453     this.locked = false;
25454     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25455 };
25456
25457
25458 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25459     /** @ignore Called by the grid automatically. Do not call directly. */
25460     init : function(grid){
25461         this.grid = grid;
25462         this.initEvents();
25463     },
25464
25465     /**
25466      * Locks the selections.
25467      */
25468     lock : function(){
25469         this.locked = true;
25470     },
25471
25472     /**
25473      * Unlocks the selections.
25474      */
25475     unlock : function(){
25476         this.locked = false;
25477     },
25478
25479     /**
25480      * Returns true if the selections are locked.
25481      * @return {Boolean}
25482      */
25483     isLocked : function(){
25484         return this.locked;
25485     },
25486     
25487     
25488     initEvents : function ()
25489     {
25490         
25491     }
25492 });
25493 /**
25494  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25495  * @class Roo.bootstrap.Table.RowSelectionModel
25496  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25497  * It supports multiple selections and keyboard selection/navigation. 
25498  * @constructor
25499  * @param {Object} config
25500  */
25501
25502 Roo.bootstrap.Table.RowSelectionModel = function(config){
25503     Roo.apply(this, config);
25504     this.selections = new Roo.util.MixedCollection(false, function(o){
25505         return o.id;
25506     });
25507
25508     this.last = false;
25509     this.lastActive = false;
25510
25511     this.addEvents({
25512         /**
25513              * @event selectionchange
25514              * Fires when the selection changes
25515              * @param {SelectionModel} this
25516              */
25517             "selectionchange" : true,
25518         /**
25519              * @event afterselectionchange
25520              * Fires after the selection changes (eg. by key press or clicking)
25521              * @param {SelectionModel} this
25522              */
25523             "afterselectionchange" : true,
25524         /**
25525              * @event beforerowselect
25526              * Fires when a row is selected being selected, return false to cancel.
25527              * @param {SelectionModel} this
25528              * @param {Number} rowIndex The selected index
25529              * @param {Boolean} keepExisting False if other selections will be cleared
25530              */
25531             "beforerowselect" : true,
25532         /**
25533              * @event rowselect
25534              * Fires when a row is selected.
25535              * @param {SelectionModel} this
25536              * @param {Number} rowIndex The selected index
25537              * @param {Roo.data.Record} r The record
25538              */
25539             "rowselect" : true,
25540         /**
25541              * @event rowdeselect
25542              * Fires when a row is deselected.
25543              * @param {SelectionModel} this
25544              * @param {Number} rowIndex The selected index
25545              */
25546         "rowdeselect" : true
25547     });
25548     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25549     this.locked = false;
25550  };
25551
25552 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25553     /**
25554      * @cfg {Boolean} singleSelect
25555      * True to allow selection of only one row at a time (defaults to false)
25556      */
25557     singleSelect : false,
25558
25559     // private
25560     initEvents : function()
25561     {
25562
25563         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25564         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25565         //}else{ // allow click to work like normal
25566          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25567         //}
25568         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25569         this.grid.on("rowclick", this.handleMouseDown, this);
25570         
25571         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25572             "up" : function(e){
25573                 if(!e.shiftKey){
25574                     this.selectPrevious(e.shiftKey);
25575                 }else if(this.last !== false && this.lastActive !== false){
25576                     var last = this.last;
25577                     this.selectRange(this.last,  this.lastActive-1);
25578                     this.grid.getView().focusRow(this.lastActive);
25579                     if(last !== false){
25580                         this.last = last;
25581                     }
25582                 }else{
25583                     this.selectFirstRow();
25584                 }
25585                 this.fireEvent("afterselectionchange", this);
25586             },
25587             "down" : function(e){
25588                 if(!e.shiftKey){
25589                     this.selectNext(e.shiftKey);
25590                 }else if(this.last !== false && this.lastActive !== false){
25591                     var last = this.last;
25592                     this.selectRange(this.last,  this.lastActive+1);
25593                     this.grid.getView().focusRow(this.lastActive);
25594                     if(last !== false){
25595                         this.last = last;
25596                     }
25597                 }else{
25598                     this.selectFirstRow();
25599                 }
25600                 this.fireEvent("afterselectionchange", this);
25601             },
25602             scope: this
25603         });
25604         this.grid.store.on('load', function(){
25605             this.selections.clear();
25606         },this);
25607         /*
25608         var view = this.grid.view;
25609         view.on("refresh", this.onRefresh, this);
25610         view.on("rowupdated", this.onRowUpdated, this);
25611         view.on("rowremoved", this.onRemove, this);
25612         */
25613     },
25614
25615     // private
25616     onRefresh : function()
25617     {
25618         var ds = this.grid.store, i, v = this.grid.view;
25619         var s = this.selections;
25620         s.each(function(r){
25621             if((i = ds.indexOfId(r.id)) != -1){
25622                 v.onRowSelect(i);
25623             }else{
25624                 s.remove(r);
25625             }
25626         });
25627     },
25628
25629     // private
25630     onRemove : function(v, index, r){
25631         this.selections.remove(r);
25632     },
25633
25634     // private
25635     onRowUpdated : function(v, index, r){
25636         if(this.isSelected(r)){
25637             v.onRowSelect(index);
25638         }
25639     },
25640
25641     /**
25642      * Select records.
25643      * @param {Array} records The records to select
25644      * @param {Boolean} keepExisting (optional) True to keep existing selections
25645      */
25646     selectRecords : function(records, keepExisting)
25647     {
25648         if(!keepExisting){
25649             this.clearSelections();
25650         }
25651             var ds = this.grid.store;
25652         for(var i = 0, len = records.length; i < len; i++){
25653             this.selectRow(ds.indexOf(records[i]), true);
25654         }
25655     },
25656
25657     /**
25658      * Gets the number of selected rows.
25659      * @return {Number}
25660      */
25661     getCount : function(){
25662         return this.selections.length;
25663     },
25664
25665     /**
25666      * Selects the first row in the grid.
25667      */
25668     selectFirstRow : function(){
25669         this.selectRow(0);
25670     },
25671
25672     /**
25673      * Select the last row.
25674      * @param {Boolean} keepExisting (optional) True to keep existing selections
25675      */
25676     selectLastRow : function(keepExisting){
25677         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25678         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25679     },
25680
25681     /**
25682      * Selects the row immediately following the last selected row.
25683      * @param {Boolean} keepExisting (optional) True to keep existing selections
25684      */
25685     selectNext : function(keepExisting)
25686     {
25687             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25688             this.selectRow(this.last+1, keepExisting);
25689             this.grid.getView().focusRow(this.last);
25690         }
25691     },
25692
25693     /**
25694      * Selects the row that precedes the last selected row.
25695      * @param {Boolean} keepExisting (optional) True to keep existing selections
25696      */
25697     selectPrevious : function(keepExisting){
25698         if(this.last){
25699             this.selectRow(this.last-1, keepExisting);
25700             this.grid.getView().focusRow(this.last);
25701         }
25702     },
25703
25704     /**
25705      * Returns the selected records
25706      * @return {Array} Array of selected records
25707      */
25708     getSelections : function(){
25709         return [].concat(this.selections.items);
25710     },
25711
25712     /**
25713      * Returns the first selected record.
25714      * @return {Record}
25715      */
25716     getSelected : function(){
25717         return this.selections.itemAt(0);
25718     },
25719
25720
25721     /**
25722      * Clears all selections.
25723      */
25724     clearSelections : function(fast)
25725     {
25726         if(this.locked) {
25727             return;
25728         }
25729         if(fast !== true){
25730                 var ds = this.grid.store;
25731             var s = this.selections;
25732             s.each(function(r){
25733                 this.deselectRow(ds.indexOfId(r.id));
25734             }, this);
25735             s.clear();
25736         }else{
25737             this.selections.clear();
25738         }
25739         this.last = false;
25740     },
25741
25742
25743     /**
25744      * Selects all rows.
25745      */
25746     selectAll : function(){
25747         if(this.locked) {
25748             return;
25749         }
25750         this.selections.clear();
25751         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25752             this.selectRow(i, true);
25753         }
25754     },
25755
25756     /**
25757      * Returns True if there is a selection.
25758      * @return {Boolean}
25759      */
25760     hasSelection : function(){
25761         return this.selections.length > 0;
25762     },
25763
25764     /**
25765      * Returns True if the specified row is selected.
25766      * @param {Number/Record} record The record or index of the record to check
25767      * @return {Boolean}
25768      */
25769     isSelected : function(index){
25770             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25771         return (r && this.selections.key(r.id) ? true : false);
25772     },
25773
25774     /**
25775      * Returns True if the specified record id is selected.
25776      * @param {String} id The id of record to check
25777      * @return {Boolean}
25778      */
25779     isIdSelected : function(id){
25780         return (this.selections.key(id) ? true : false);
25781     },
25782
25783
25784     // private
25785     handleMouseDBClick : function(e, t){
25786         
25787     },
25788     // private
25789     handleMouseDown : function(e, t)
25790     {
25791             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25792         if(this.isLocked() || rowIndex < 0 ){
25793             return;
25794         };
25795         if(e.shiftKey && this.last !== false){
25796             var last = this.last;
25797             this.selectRange(last, rowIndex, e.ctrlKey);
25798             this.last = last; // reset the last
25799             t.focus();
25800     
25801         }else{
25802             var isSelected = this.isSelected(rowIndex);
25803             //Roo.log("select row:" + rowIndex);
25804             if(isSelected){
25805                 this.deselectRow(rowIndex);
25806             } else {
25807                         this.selectRow(rowIndex, true);
25808             }
25809     
25810             /*
25811                 if(e.button !== 0 && isSelected){
25812                 alert('rowIndex 2: ' + rowIndex);
25813                     view.focusRow(rowIndex);
25814                 }else if(e.ctrlKey && isSelected){
25815                     this.deselectRow(rowIndex);
25816                 }else if(!isSelected){
25817                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25818                     view.focusRow(rowIndex);
25819                 }
25820             */
25821         }
25822         this.fireEvent("afterselectionchange", this);
25823     },
25824     // private
25825     handleDragableRowClick :  function(grid, rowIndex, e) 
25826     {
25827         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25828             this.selectRow(rowIndex, false);
25829             grid.view.focusRow(rowIndex);
25830              this.fireEvent("afterselectionchange", this);
25831         }
25832     },
25833     
25834     /**
25835      * Selects multiple rows.
25836      * @param {Array} rows Array of the indexes of the row to select
25837      * @param {Boolean} keepExisting (optional) True to keep existing selections
25838      */
25839     selectRows : function(rows, keepExisting){
25840         if(!keepExisting){
25841             this.clearSelections();
25842         }
25843         for(var i = 0, len = rows.length; i < len; i++){
25844             this.selectRow(rows[i], true);
25845         }
25846     },
25847
25848     /**
25849      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25850      * @param {Number} startRow The index of the first row in the range
25851      * @param {Number} endRow The index of the last row in the range
25852      * @param {Boolean} keepExisting (optional) True to retain existing selections
25853      */
25854     selectRange : function(startRow, endRow, keepExisting){
25855         if(this.locked) {
25856             return;
25857         }
25858         if(!keepExisting){
25859             this.clearSelections();
25860         }
25861         if(startRow <= endRow){
25862             for(var i = startRow; i <= endRow; i++){
25863                 this.selectRow(i, true);
25864             }
25865         }else{
25866             for(var i = startRow; i >= endRow; i--){
25867                 this.selectRow(i, true);
25868             }
25869         }
25870     },
25871
25872     /**
25873      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25874      * @param {Number} startRow The index of the first row in the range
25875      * @param {Number} endRow The index of the last row in the range
25876      */
25877     deselectRange : function(startRow, endRow, preventViewNotify){
25878         if(this.locked) {
25879             return;
25880         }
25881         for(var i = startRow; i <= endRow; i++){
25882             this.deselectRow(i, preventViewNotify);
25883         }
25884     },
25885
25886     /**
25887      * Selects a row.
25888      * @param {Number} row The index of the row to select
25889      * @param {Boolean} keepExisting (optional) True to keep existing selections
25890      */
25891     selectRow : function(index, keepExisting, preventViewNotify)
25892     {
25893             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25894             return;
25895         }
25896         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25897             if(!keepExisting || this.singleSelect){
25898                 this.clearSelections();
25899             }
25900             
25901             var r = this.grid.store.getAt(index);
25902             //console.log('selectRow - record id :' + r.id);
25903             
25904             this.selections.add(r);
25905             this.last = this.lastActive = index;
25906             if(!preventViewNotify){
25907                 var proxy = new Roo.Element(
25908                                 this.grid.getRowDom(index)
25909                 );
25910                 proxy.addClass('bg-info info');
25911             }
25912             this.fireEvent("rowselect", this, index, r);
25913             this.fireEvent("selectionchange", this);
25914         }
25915     },
25916
25917     /**
25918      * Deselects a row.
25919      * @param {Number} row The index of the row to deselect
25920      */
25921     deselectRow : function(index, preventViewNotify)
25922     {
25923         if(this.locked) {
25924             return;
25925         }
25926         if(this.last == index){
25927             this.last = false;
25928         }
25929         if(this.lastActive == index){
25930             this.lastActive = false;
25931         }
25932         
25933         var r = this.grid.store.getAt(index);
25934         if (!r) {
25935             return;
25936         }
25937         
25938         this.selections.remove(r);
25939         //.console.log('deselectRow - record id :' + r.id);
25940         if(!preventViewNotify){
25941         
25942             var proxy = new Roo.Element(
25943                 this.grid.getRowDom(index)
25944             );
25945             proxy.removeClass('bg-info info');
25946         }
25947         this.fireEvent("rowdeselect", this, index);
25948         this.fireEvent("selectionchange", this);
25949     },
25950
25951     // private
25952     restoreLast : function(){
25953         if(this._last){
25954             this.last = this._last;
25955         }
25956     },
25957
25958     // private
25959     acceptsNav : function(row, col, cm){
25960         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25961     },
25962
25963     // private
25964     onEditorKey : function(field, e){
25965         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25966         if(k == e.TAB){
25967             e.stopEvent();
25968             ed.completeEdit();
25969             if(e.shiftKey){
25970                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25971             }else{
25972                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25973             }
25974         }else if(k == e.ENTER && !e.ctrlKey){
25975             e.stopEvent();
25976             ed.completeEdit();
25977             if(e.shiftKey){
25978                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25979             }else{
25980                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25981             }
25982         }else if(k == e.ESC){
25983             ed.cancelEdit();
25984         }
25985         if(newCell){
25986             g.startEditing(newCell[0], newCell[1]);
25987         }
25988     }
25989 });
25990 /*
25991  * Based on:
25992  * Ext JS Library 1.1.1
25993  * Copyright(c) 2006-2007, Ext JS, LLC.
25994  *
25995  * Originally Released Under LGPL - original licence link has changed is not relivant.
25996  *
25997  * Fork - LGPL
25998  * <script type="text/javascript">
25999  */
26000  
26001 /**
26002  * @class Roo.bootstrap.PagingToolbar
26003  * @extends Roo.bootstrap.NavSimplebar
26004  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26005  * @constructor
26006  * Create a new PagingToolbar
26007  * @param {Object} config The config object
26008  * @param {Roo.data.Store} store
26009  */
26010 Roo.bootstrap.PagingToolbar = function(config)
26011 {
26012     // old args format still supported... - xtype is prefered..
26013         // created from xtype...
26014     
26015     this.ds = config.dataSource;
26016     
26017     if (config.store && !this.ds) {
26018         this.store= Roo.factory(config.store, Roo.data);
26019         this.ds = this.store;
26020         this.ds.xmodule = this.xmodule || false;
26021     }
26022     
26023     this.toolbarItems = [];
26024     if (config.items) {
26025         this.toolbarItems = config.items;
26026     }
26027     
26028     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26029     
26030     this.cursor = 0;
26031     
26032     if (this.ds) { 
26033         this.bind(this.ds);
26034     }
26035     
26036     if (Roo.bootstrap.version == 4) {
26037         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26038     } else {
26039         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26040     }
26041     
26042 };
26043
26044 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26045     /**
26046      * @cfg {Roo.data.Store} dataSource
26047      * The underlying data store providing the paged data
26048      */
26049     /**
26050      * @cfg {String/HTMLElement/Element} container
26051      * container The id or element that will contain the toolbar
26052      */
26053     /**
26054      * @cfg {Boolean} displayInfo
26055      * True to display the displayMsg (defaults to false)
26056      */
26057     /**
26058      * @cfg {Number} pageSize
26059      * The number of records to display per page (defaults to 20)
26060      */
26061     pageSize: 20,
26062     /**
26063      * @cfg {String} displayMsg
26064      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26065      */
26066     displayMsg : 'Displaying {0} - {1} of {2}',
26067     /**
26068      * @cfg {String} emptyMsg
26069      * The message to display when no records are found (defaults to "No data to display")
26070      */
26071     emptyMsg : 'No data to display',
26072     /**
26073      * Customizable piece of the default paging text (defaults to "Page")
26074      * @type String
26075      */
26076     beforePageText : "Page",
26077     /**
26078      * Customizable piece of the default paging text (defaults to "of %0")
26079      * @type String
26080      */
26081     afterPageText : "of {0}",
26082     /**
26083      * Customizable piece of the default paging text (defaults to "First Page")
26084      * @type String
26085      */
26086     firstText : "First Page",
26087     /**
26088      * Customizable piece of the default paging text (defaults to "Previous Page")
26089      * @type String
26090      */
26091     prevText : "Previous Page",
26092     /**
26093      * Customizable piece of the default paging text (defaults to "Next Page")
26094      * @type String
26095      */
26096     nextText : "Next Page",
26097     /**
26098      * Customizable piece of the default paging text (defaults to "Last Page")
26099      * @type String
26100      */
26101     lastText : "Last Page",
26102     /**
26103      * Customizable piece of the default paging text (defaults to "Refresh")
26104      * @type String
26105      */
26106     refreshText : "Refresh",
26107
26108     buttons : false,
26109     // private
26110     onRender : function(ct, position) 
26111     {
26112         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26113         this.navgroup.parentId = this.id;
26114         this.navgroup.onRender(this.el, null);
26115         // add the buttons to the navgroup
26116         
26117         if(this.displayInfo){
26118             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26119             this.displayEl = this.el.select('.x-paging-info', true).first();
26120 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26121 //            this.displayEl = navel.el.select('span',true).first();
26122         }
26123         
26124         var _this = this;
26125         
26126         if(this.buttons){
26127             Roo.each(_this.buttons, function(e){ // this might need to use render????
26128                Roo.factory(e).render(_this.el);
26129             });
26130         }
26131             
26132         Roo.each(_this.toolbarItems, function(e) {
26133             _this.navgroup.addItem(e);
26134         });
26135         
26136         
26137         this.first = this.navgroup.addItem({
26138             tooltip: this.firstText,
26139             cls: "prev btn-outline-secondary",
26140             html : ' <i class="fa fa-step-backward"></i>',
26141             disabled: true,
26142             preventDefault: true,
26143             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26144         });
26145         
26146         this.prev =  this.navgroup.addItem({
26147             tooltip: this.prevText,
26148             cls: "prev btn-outline-secondary",
26149             html : ' <i class="fa fa-backward"></i>',
26150             disabled: true,
26151             preventDefault: true,
26152             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26153         });
26154     //this.addSeparator();
26155         
26156         
26157         var field = this.navgroup.addItem( {
26158             tagtype : 'span',
26159             cls : 'x-paging-position  btn-outline-secondary',
26160              disabled: true,
26161             html : this.beforePageText  +
26162                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26163                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26164          } ); //?? escaped?
26165         
26166         this.field = field.el.select('input', true).first();
26167         this.field.on("keydown", this.onPagingKeydown, this);
26168         this.field.on("focus", function(){this.dom.select();});
26169     
26170     
26171         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26172         //this.field.setHeight(18);
26173         //this.addSeparator();
26174         this.next = this.navgroup.addItem({
26175             tooltip: this.nextText,
26176             cls: "next btn-outline-secondary",
26177             html : ' <i class="fa fa-forward"></i>',
26178             disabled: true,
26179             preventDefault: true,
26180             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26181         });
26182         this.last = this.navgroup.addItem({
26183             tooltip: this.lastText,
26184             html : ' <i class="fa fa-step-forward"></i>',
26185             cls: "next btn-outline-secondary",
26186             disabled: true,
26187             preventDefault: true,
26188             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26189         });
26190     //this.addSeparator();
26191         this.loading = this.navgroup.addItem({
26192             tooltip: this.refreshText,
26193             cls: "btn-outline-secondary",
26194             html : ' <i class="fa fa-refresh"></i>',
26195             preventDefault: true,
26196             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26197         });
26198         
26199     },
26200
26201     // private
26202     updateInfo : function(){
26203         if(this.displayEl){
26204             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26205             var msg = count == 0 ?
26206                 this.emptyMsg :
26207                 String.format(
26208                     this.displayMsg,
26209                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26210                 );
26211             this.displayEl.update(msg);
26212         }
26213     },
26214
26215     // private
26216     onLoad : function(ds, r, o)
26217     {
26218         this.cursor = o.params.start ? o.params.start : 0;
26219         
26220         var d = this.getPageData(),
26221             ap = d.activePage,
26222             ps = d.pages;
26223         
26224         
26225         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26226         this.field.dom.value = ap;
26227         this.first.setDisabled(ap == 1);
26228         this.prev.setDisabled(ap == 1);
26229         this.next.setDisabled(ap == ps);
26230         this.last.setDisabled(ap == ps);
26231         this.loading.enable();
26232         this.updateInfo();
26233     },
26234
26235     // private
26236     getPageData : function(){
26237         var total = this.ds.getTotalCount();
26238         return {
26239             total : total,
26240             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26241             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26242         };
26243     },
26244
26245     // private
26246     onLoadError : function(){
26247         this.loading.enable();
26248     },
26249
26250     // private
26251     onPagingKeydown : function(e){
26252         var k = e.getKey();
26253         var d = this.getPageData();
26254         if(k == e.RETURN){
26255             var v = this.field.dom.value, pageNum;
26256             if(!v || isNaN(pageNum = parseInt(v, 10))){
26257                 this.field.dom.value = d.activePage;
26258                 return;
26259             }
26260             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26261             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26262             e.stopEvent();
26263         }
26264         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))
26265         {
26266           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26267           this.field.dom.value = pageNum;
26268           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26269           e.stopEvent();
26270         }
26271         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26272         {
26273           var v = this.field.dom.value, pageNum; 
26274           var increment = (e.shiftKey) ? 10 : 1;
26275           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26276                 increment *= -1;
26277           }
26278           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26279             this.field.dom.value = d.activePage;
26280             return;
26281           }
26282           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26283           {
26284             this.field.dom.value = parseInt(v, 10) + increment;
26285             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26286             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26287           }
26288           e.stopEvent();
26289         }
26290     },
26291
26292     // private
26293     beforeLoad : function(){
26294         if(this.loading){
26295             this.loading.disable();
26296         }
26297     },
26298
26299     // private
26300     onClick : function(which){
26301         
26302         var ds = this.ds;
26303         if (!ds) {
26304             return;
26305         }
26306         
26307         switch(which){
26308             case "first":
26309                 ds.load({params:{start: 0, limit: this.pageSize}});
26310             break;
26311             case "prev":
26312                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26313             break;
26314             case "next":
26315                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26316             break;
26317             case "last":
26318                 var total = ds.getTotalCount();
26319                 var extra = total % this.pageSize;
26320                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26321                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26322             break;
26323             case "refresh":
26324                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26325             break;
26326         }
26327     },
26328
26329     /**
26330      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26331      * @param {Roo.data.Store} store The data store to unbind
26332      */
26333     unbind : function(ds){
26334         ds.un("beforeload", this.beforeLoad, this);
26335         ds.un("load", this.onLoad, this);
26336         ds.un("loadexception", this.onLoadError, this);
26337         ds.un("remove", this.updateInfo, this);
26338         ds.un("add", this.updateInfo, this);
26339         this.ds = undefined;
26340     },
26341
26342     /**
26343      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26344      * @param {Roo.data.Store} store The data store to bind
26345      */
26346     bind : function(ds){
26347         ds.on("beforeload", this.beforeLoad, this);
26348         ds.on("load", this.onLoad, this);
26349         ds.on("loadexception", this.onLoadError, this);
26350         ds.on("remove", this.updateInfo, this);
26351         ds.on("add", this.updateInfo, this);
26352         this.ds = ds;
26353     }
26354 });/*
26355  * - LGPL
26356  *
26357  * element
26358  * 
26359  */
26360
26361 /**
26362  * @class Roo.bootstrap.MessageBar
26363  * @extends Roo.bootstrap.Component
26364  * Bootstrap MessageBar class
26365  * @cfg {String} html contents of the MessageBar
26366  * @cfg {String} weight (info | success | warning | danger) default info
26367  * @cfg {String} beforeClass insert the bar before the given class
26368  * @cfg {Boolean} closable (true | false) default false
26369  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26370  * 
26371  * @constructor
26372  * Create a new Element
26373  * @param {Object} config The config object
26374  */
26375
26376 Roo.bootstrap.MessageBar = function(config){
26377     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26378 };
26379
26380 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26381     
26382     html: '',
26383     weight: 'info',
26384     closable: false,
26385     fixed: false,
26386     beforeClass: 'bootstrap-sticky-wrap',
26387     
26388     getAutoCreate : function(){
26389         
26390         var cfg = {
26391             tag: 'div',
26392             cls: 'alert alert-dismissable alert-' + this.weight,
26393             cn: [
26394                 {
26395                     tag: 'span',
26396                     cls: 'message',
26397                     html: this.html || ''
26398                 }
26399             ]
26400         };
26401         
26402         if(this.fixed){
26403             cfg.cls += ' alert-messages-fixed';
26404         }
26405         
26406         if(this.closable){
26407             cfg.cn.push({
26408                 tag: 'button',
26409                 cls: 'close',
26410                 html: 'x'
26411             });
26412         }
26413         
26414         return cfg;
26415     },
26416     
26417     onRender : function(ct, position)
26418     {
26419         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26420         
26421         if(!this.el){
26422             var cfg = Roo.apply({},  this.getAutoCreate());
26423             cfg.id = Roo.id();
26424             
26425             if (this.cls) {
26426                 cfg.cls += ' ' + this.cls;
26427             }
26428             if (this.style) {
26429                 cfg.style = this.style;
26430             }
26431             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26432             
26433             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26434         }
26435         
26436         this.el.select('>button.close').on('click', this.hide, this);
26437         
26438     },
26439     
26440     show : function()
26441     {
26442         if (!this.rendered) {
26443             this.render();
26444         }
26445         
26446         this.el.show();
26447         
26448         this.fireEvent('show', this);
26449         
26450     },
26451     
26452     hide : function()
26453     {
26454         if (!this.rendered) {
26455             this.render();
26456         }
26457         
26458         this.el.hide();
26459         
26460         this.fireEvent('hide', this);
26461     },
26462     
26463     update : function()
26464     {
26465 //        var e = this.el.dom.firstChild;
26466 //        
26467 //        if(this.closable){
26468 //            e = e.nextSibling;
26469 //        }
26470 //        
26471 //        e.data = this.html || '';
26472
26473         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26474     }
26475    
26476 });
26477
26478  
26479
26480      /*
26481  * - LGPL
26482  *
26483  * Graph
26484  * 
26485  */
26486
26487
26488 /**
26489  * @class Roo.bootstrap.Graph
26490  * @extends Roo.bootstrap.Component
26491  * Bootstrap Graph class
26492 > Prameters
26493  -sm {number} sm 4
26494  -md {number} md 5
26495  @cfg {String} graphtype  bar | vbar | pie
26496  @cfg {number} g_x coodinator | centre x (pie)
26497  @cfg {number} g_y coodinator | centre y (pie)
26498  @cfg {number} g_r radius (pie)
26499  @cfg {number} g_height height of the chart (respected by all elements in the set)
26500  @cfg {number} g_width width of the chart (respected by all elements in the set)
26501  @cfg {Object} title The title of the chart
26502     
26503  -{Array}  values
26504  -opts (object) options for the chart 
26505      o {
26506      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26507      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26508      o vgutter (number)
26509      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.
26510      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26511      o to
26512      o stretch (boolean)
26513      o }
26514  -opts (object) options for the pie
26515      o{
26516      o cut
26517      o startAngle (number)
26518      o endAngle (number)
26519      } 
26520  *
26521  * @constructor
26522  * Create a new Input
26523  * @param {Object} config The config object
26524  */
26525
26526 Roo.bootstrap.Graph = function(config){
26527     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26528     
26529     this.addEvents({
26530         // img events
26531         /**
26532          * @event click
26533          * The img click event for the img.
26534          * @param {Roo.EventObject} e
26535          */
26536         "click" : true
26537     });
26538 };
26539
26540 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26541     
26542     sm: 4,
26543     md: 5,
26544     graphtype: 'bar',
26545     g_height: 250,
26546     g_width: 400,
26547     g_x: 50,
26548     g_y: 50,
26549     g_r: 30,
26550     opts:{
26551         //g_colors: this.colors,
26552         g_type: 'soft',
26553         g_gutter: '20%'
26554
26555     },
26556     title : false,
26557
26558     getAutoCreate : function(){
26559         
26560         var cfg = {
26561             tag: 'div',
26562             html : null
26563         };
26564         
26565         
26566         return  cfg;
26567     },
26568
26569     onRender : function(ct,position){
26570         
26571         
26572         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26573         
26574         if (typeof(Raphael) == 'undefined') {
26575             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26576             return;
26577         }
26578         
26579         this.raphael = Raphael(this.el.dom);
26580         
26581                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26582                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26583                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26584                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26585                 /*
26586                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26587                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26588                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26589                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26590                 
26591                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26592                 r.barchart(330, 10, 300, 220, data1);
26593                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26594                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26595                 */
26596                 
26597                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26598                 // r.barchart(30, 30, 560, 250,  xdata, {
26599                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26600                 //     axis : "0 0 1 1",
26601                 //     axisxlabels :  xdata
26602                 //     //yvalues : cols,
26603                    
26604                 // });
26605 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26606 //        
26607 //        this.load(null,xdata,{
26608 //                axis : "0 0 1 1",
26609 //                axisxlabels :  xdata
26610 //                });
26611
26612     },
26613
26614     load : function(graphtype,xdata,opts)
26615     {
26616         this.raphael.clear();
26617         if(!graphtype) {
26618             graphtype = this.graphtype;
26619         }
26620         if(!opts){
26621             opts = this.opts;
26622         }
26623         var r = this.raphael,
26624             fin = function () {
26625                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26626             },
26627             fout = function () {
26628                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26629             },
26630             pfin = function() {
26631                 this.sector.stop();
26632                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26633
26634                 if (this.label) {
26635                     this.label[0].stop();
26636                     this.label[0].attr({ r: 7.5 });
26637                     this.label[1].attr({ "font-weight": 800 });
26638                 }
26639             },
26640             pfout = function() {
26641                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26642
26643                 if (this.label) {
26644                     this.label[0].animate({ r: 5 }, 500, "bounce");
26645                     this.label[1].attr({ "font-weight": 400 });
26646                 }
26647             };
26648
26649         switch(graphtype){
26650             case 'bar':
26651                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26652                 break;
26653             case 'hbar':
26654                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26655                 break;
26656             case 'pie':
26657 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26658 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26659 //            
26660                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26661                 
26662                 break;
26663
26664         }
26665         
26666         if(this.title){
26667             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26668         }
26669         
26670     },
26671     
26672     setTitle: function(o)
26673     {
26674         this.title = o;
26675     },
26676     
26677     initEvents: function() {
26678         
26679         if(!this.href){
26680             this.el.on('click', this.onClick, this);
26681         }
26682     },
26683     
26684     onClick : function(e)
26685     {
26686         Roo.log('img onclick');
26687         this.fireEvent('click', this, e);
26688     }
26689    
26690 });
26691
26692  
26693 /*
26694  * - LGPL
26695  *
26696  * numberBox
26697  * 
26698  */
26699 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26700
26701 /**
26702  * @class Roo.bootstrap.dash.NumberBox
26703  * @extends Roo.bootstrap.Component
26704  * Bootstrap NumberBox class
26705  * @cfg {String} headline Box headline
26706  * @cfg {String} content Box content
26707  * @cfg {String} icon Box icon
26708  * @cfg {String} footer Footer text
26709  * @cfg {String} fhref Footer href
26710  * 
26711  * @constructor
26712  * Create a new NumberBox
26713  * @param {Object} config The config object
26714  */
26715
26716
26717 Roo.bootstrap.dash.NumberBox = function(config){
26718     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26719     
26720 };
26721
26722 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26723     
26724     headline : '',
26725     content : '',
26726     icon : '',
26727     footer : '',
26728     fhref : '',
26729     ficon : '',
26730     
26731     getAutoCreate : function(){
26732         
26733         var cfg = {
26734             tag : 'div',
26735             cls : 'small-box ',
26736             cn : [
26737                 {
26738                     tag : 'div',
26739                     cls : 'inner',
26740                     cn :[
26741                         {
26742                             tag : 'h3',
26743                             cls : 'roo-headline',
26744                             html : this.headline
26745                         },
26746                         {
26747                             tag : 'p',
26748                             cls : 'roo-content',
26749                             html : this.content
26750                         }
26751                     ]
26752                 }
26753             ]
26754         };
26755         
26756         if(this.icon){
26757             cfg.cn.push({
26758                 tag : 'div',
26759                 cls : 'icon',
26760                 cn :[
26761                     {
26762                         tag : 'i',
26763                         cls : 'ion ' + this.icon
26764                     }
26765                 ]
26766             });
26767         }
26768         
26769         if(this.footer){
26770             var footer = {
26771                 tag : 'a',
26772                 cls : 'small-box-footer',
26773                 href : this.fhref || '#',
26774                 html : this.footer
26775             };
26776             
26777             cfg.cn.push(footer);
26778             
26779         }
26780         
26781         return  cfg;
26782     },
26783
26784     onRender : function(ct,position){
26785         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26786
26787
26788        
26789                 
26790     },
26791
26792     setHeadline: function (value)
26793     {
26794         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26795     },
26796     
26797     setFooter: function (value, href)
26798     {
26799         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26800         
26801         if(href){
26802             this.el.select('a.small-box-footer',true).first().attr('href', href);
26803         }
26804         
26805     },
26806
26807     setContent: function (value)
26808     {
26809         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26810     },
26811
26812     initEvents: function() 
26813     {   
26814         
26815     }
26816     
26817 });
26818
26819  
26820 /*
26821  * - LGPL
26822  *
26823  * TabBox
26824  * 
26825  */
26826 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26827
26828 /**
26829  * @class Roo.bootstrap.dash.TabBox
26830  * @extends Roo.bootstrap.Component
26831  * Bootstrap TabBox class
26832  * @cfg {String} title Title of the TabBox
26833  * @cfg {String} icon Icon of the TabBox
26834  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26835  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26836  * 
26837  * @constructor
26838  * Create a new TabBox
26839  * @param {Object} config The config object
26840  */
26841
26842
26843 Roo.bootstrap.dash.TabBox = function(config){
26844     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26845     this.addEvents({
26846         // raw events
26847         /**
26848          * @event addpane
26849          * When a pane is added
26850          * @param {Roo.bootstrap.dash.TabPane} pane
26851          */
26852         "addpane" : true,
26853         /**
26854          * @event activatepane
26855          * When a pane is activated
26856          * @param {Roo.bootstrap.dash.TabPane} pane
26857          */
26858         "activatepane" : true
26859         
26860          
26861     });
26862     
26863     this.panes = [];
26864 };
26865
26866 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26867
26868     title : '',
26869     icon : false,
26870     showtabs : true,
26871     tabScrollable : false,
26872     
26873     getChildContainer : function()
26874     {
26875         return this.el.select('.tab-content', true).first();
26876     },
26877     
26878     getAutoCreate : function(){
26879         
26880         var header = {
26881             tag: 'li',
26882             cls: 'pull-left header',
26883             html: this.title,
26884             cn : []
26885         };
26886         
26887         if(this.icon){
26888             header.cn.push({
26889                 tag: 'i',
26890                 cls: 'fa ' + this.icon
26891             });
26892         }
26893         
26894         var h = {
26895             tag: 'ul',
26896             cls: 'nav nav-tabs pull-right',
26897             cn: [
26898                 header
26899             ]
26900         };
26901         
26902         if(this.tabScrollable){
26903             h = {
26904                 tag: 'div',
26905                 cls: 'tab-header',
26906                 cn: [
26907                     {
26908                         tag: 'ul',
26909                         cls: 'nav nav-tabs pull-right',
26910                         cn: [
26911                             header
26912                         ]
26913                     }
26914                 ]
26915             };
26916         }
26917         
26918         var cfg = {
26919             tag: 'div',
26920             cls: 'nav-tabs-custom',
26921             cn: [
26922                 h,
26923                 {
26924                     tag: 'div',
26925                     cls: 'tab-content no-padding',
26926                     cn: []
26927                 }
26928             ]
26929         };
26930
26931         return  cfg;
26932     },
26933     initEvents : function()
26934     {
26935         //Roo.log('add add pane handler');
26936         this.on('addpane', this.onAddPane, this);
26937     },
26938      /**
26939      * Updates the box title
26940      * @param {String} html to set the title to.
26941      */
26942     setTitle : function(value)
26943     {
26944         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26945     },
26946     onAddPane : function(pane)
26947     {
26948         this.panes.push(pane);
26949         //Roo.log('addpane');
26950         //Roo.log(pane);
26951         // tabs are rendere left to right..
26952         if(!this.showtabs){
26953             return;
26954         }
26955         
26956         var ctr = this.el.select('.nav-tabs', true).first();
26957          
26958          
26959         var existing = ctr.select('.nav-tab',true);
26960         var qty = existing.getCount();;
26961         
26962         
26963         var tab = ctr.createChild({
26964             tag : 'li',
26965             cls : 'nav-tab' + (qty ? '' : ' active'),
26966             cn : [
26967                 {
26968                     tag : 'a',
26969                     href:'#',
26970                     html : pane.title
26971                 }
26972             ]
26973         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26974         pane.tab = tab;
26975         
26976         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26977         if (!qty) {
26978             pane.el.addClass('active');
26979         }
26980         
26981                 
26982     },
26983     onTabClick : function(ev,un,ob,pane)
26984     {
26985         //Roo.log('tab - prev default');
26986         ev.preventDefault();
26987         
26988         
26989         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26990         pane.tab.addClass('active');
26991         //Roo.log(pane.title);
26992         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26993         // technically we should have a deactivate event.. but maybe add later.
26994         // and it should not de-activate the selected tab...
26995         this.fireEvent('activatepane', pane);
26996         pane.el.addClass('active');
26997         pane.fireEvent('activate');
26998         
26999         
27000     },
27001     
27002     getActivePane : function()
27003     {
27004         var r = false;
27005         Roo.each(this.panes, function(p) {
27006             if(p.el.hasClass('active')){
27007                 r = p;
27008                 return false;
27009             }
27010             
27011             return;
27012         });
27013         
27014         return r;
27015     }
27016     
27017     
27018 });
27019
27020  
27021 /*
27022  * - LGPL
27023  *
27024  * Tab pane
27025  * 
27026  */
27027 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27028 /**
27029  * @class Roo.bootstrap.TabPane
27030  * @extends Roo.bootstrap.Component
27031  * Bootstrap TabPane class
27032  * @cfg {Boolean} active (false | true) Default false
27033  * @cfg {String} title title of panel
27034
27035  * 
27036  * @constructor
27037  * Create a new TabPane
27038  * @param {Object} config The config object
27039  */
27040
27041 Roo.bootstrap.dash.TabPane = function(config){
27042     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27043     
27044     this.addEvents({
27045         // raw events
27046         /**
27047          * @event activate
27048          * When a pane is activated
27049          * @param {Roo.bootstrap.dash.TabPane} pane
27050          */
27051         "activate" : true
27052          
27053     });
27054 };
27055
27056 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27057     
27058     active : false,
27059     title : '',
27060     
27061     // the tabBox that this is attached to.
27062     tab : false,
27063      
27064     getAutoCreate : function() 
27065     {
27066         var cfg = {
27067             tag: 'div',
27068             cls: 'tab-pane'
27069         };
27070         
27071         if(this.active){
27072             cfg.cls += ' active';
27073         }
27074         
27075         return cfg;
27076     },
27077     initEvents  : function()
27078     {
27079         //Roo.log('trigger add pane handler');
27080         this.parent().fireEvent('addpane', this)
27081     },
27082     
27083      /**
27084      * Updates the tab title 
27085      * @param {String} html to set the title to.
27086      */
27087     setTitle: function(str)
27088     {
27089         if (!this.tab) {
27090             return;
27091         }
27092         this.title = str;
27093         this.tab.select('a', true).first().dom.innerHTML = str;
27094         
27095     }
27096     
27097     
27098     
27099 });
27100
27101  
27102
27103
27104  /*
27105  * - LGPL
27106  *
27107  * menu
27108  * 
27109  */
27110 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27111
27112 /**
27113  * @class Roo.bootstrap.menu.Menu
27114  * @extends Roo.bootstrap.Component
27115  * Bootstrap Menu class - container for Menu
27116  * @cfg {String} html Text of the menu
27117  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27118  * @cfg {String} icon Font awesome icon
27119  * @cfg {String} pos Menu align to (top | bottom) default bottom
27120  * 
27121  * 
27122  * @constructor
27123  * Create a new Menu
27124  * @param {Object} config The config object
27125  */
27126
27127
27128 Roo.bootstrap.menu.Menu = function(config){
27129     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27130     
27131     this.addEvents({
27132         /**
27133          * @event beforeshow
27134          * Fires before this menu is displayed
27135          * @param {Roo.bootstrap.menu.Menu} this
27136          */
27137         beforeshow : true,
27138         /**
27139          * @event beforehide
27140          * Fires before this menu is hidden
27141          * @param {Roo.bootstrap.menu.Menu} this
27142          */
27143         beforehide : true,
27144         /**
27145          * @event show
27146          * Fires after this menu is displayed
27147          * @param {Roo.bootstrap.menu.Menu} this
27148          */
27149         show : true,
27150         /**
27151          * @event hide
27152          * Fires after this menu is hidden
27153          * @param {Roo.bootstrap.menu.Menu} this
27154          */
27155         hide : true,
27156         /**
27157          * @event click
27158          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27159          * @param {Roo.bootstrap.menu.Menu} this
27160          * @param {Roo.EventObject} e
27161          */
27162         click : true
27163     });
27164     
27165 };
27166
27167 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27168     
27169     submenu : false,
27170     html : '',
27171     weight : 'default',
27172     icon : false,
27173     pos : 'bottom',
27174     
27175     
27176     getChildContainer : function() {
27177         if(this.isSubMenu){
27178             return this.el;
27179         }
27180         
27181         return this.el.select('ul.dropdown-menu', true).first();  
27182     },
27183     
27184     getAutoCreate : function()
27185     {
27186         var text = [
27187             {
27188                 tag : 'span',
27189                 cls : 'roo-menu-text',
27190                 html : this.html
27191             }
27192         ];
27193         
27194         if(this.icon){
27195             text.unshift({
27196                 tag : 'i',
27197                 cls : 'fa ' + this.icon
27198             })
27199         }
27200         
27201         
27202         var cfg = {
27203             tag : 'div',
27204             cls : 'btn-group',
27205             cn : [
27206                 {
27207                     tag : 'button',
27208                     cls : 'dropdown-button btn btn-' + this.weight,
27209                     cn : text
27210                 },
27211                 {
27212                     tag : 'button',
27213                     cls : 'dropdown-toggle btn btn-' + this.weight,
27214                     cn : [
27215                         {
27216                             tag : 'span',
27217                             cls : 'caret'
27218                         }
27219                     ]
27220                 },
27221                 {
27222                     tag : 'ul',
27223                     cls : 'dropdown-menu'
27224                 }
27225             ]
27226             
27227         };
27228         
27229         if(this.pos == 'top'){
27230             cfg.cls += ' dropup';
27231         }
27232         
27233         if(this.isSubMenu){
27234             cfg = {
27235                 tag : 'ul',
27236                 cls : 'dropdown-menu'
27237             }
27238         }
27239         
27240         return cfg;
27241     },
27242     
27243     onRender : function(ct, position)
27244     {
27245         this.isSubMenu = ct.hasClass('dropdown-submenu');
27246         
27247         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27248     },
27249     
27250     initEvents : function() 
27251     {
27252         if(this.isSubMenu){
27253             return;
27254         }
27255         
27256         this.hidden = true;
27257         
27258         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27259         this.triggerEl.on('click', this.onTriggerPress, this);
27260         
27261         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27262         this.buttonEl.on('click', this.onClick, this);
27263         
27264     },
27265     
27266     list : function()
27267     {
27268         if(this.isSubMenu){
27269             return this.el;
27270         }
27271         
27272         return this.el.select('ul.dropdown-menu', true).first();
27273     },
27274     
27275     onClick : function(e)
27276     {
27277         this.fireEvent("click", this, e);
27278     },
27279     
27280     onTriggerPress  : function(e)
27281     {   
27282         if (this.isVisible()) {
27283             this.hide();
27284         } else {
27285             this.show();
27286         }
27287     },
27288     
27289     isVisible : function(){
27290         return !this.hidden;
27291     },
27292     
27293     show : function()
27294     {
27295         this.fireEvent("beforeshow", this);
27296         
27297         this.hidden = false;
27298         this.el.addClass('open');
27299         
27300         Roo.get(document).on("mouseup", this.onMouseUp, this);
27301         
27302         this.fireEvent("show", this);
27303         
27304         
27305     },
27306     
27307     hide : function()
27308     {
27309         this.fireEvent("beforehide", this);
27310         
27311         this.hidden = true;
27312         this.el.removeClass('open');
27313         
27314         Roo.get(document).un("mouseup", this.onMouseUp);
27315         
27316         this.fireEvent("hide", this);
27317     },
27318     
27319     onMouseUp : function()
27320     {
27321         this.hide();
27322     }
27323     
27324 });
27325
27326  
27327  /*
27328  * - LGPL
27329  *
27330  * menu item
27331  * 
27332  */
27333 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27334
27335 /**
27336  * @class Roo.bootstrap.menu.Item
27337  * @extends Roo.bootstrap.Component
27338  * Bootstrap MenuItem class
27339  * @cfg {Boolean} submenu (true | false) default false
27340  * @cfg {String} html text of the item
27341  * @cfg {String} href the link
27342  * @cfg {Boolean} disable (true | false) default false
27343  * @cfg {Boolean} preventDefault (true | false) default true
27344  * @cfg {String} icon Font awesome icon
27345  * @cfg {String} pos Submenu align to (left | right) default right 
27346  * 
27347  * 
27348  * @constructor
27349  * Create a new Item
27350  * @param {Object} config The config object
27351  */
27352
27353
27354 Roo.bootstrap.menu.Item = function(config){
27355     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27356     this.addEvents({
27357         /**
27358          * @event mouseover
27359          * Fires when the mouse is hovering over this menu
27360          * @param {Roo.bootstrap.menu.Item} this
27361          * @param {Roo.EventObject} e
27362          */
27363         mouseover : true,
27364         /**
27365          * @event mouseout
27366          * Fires when the mouse exits this menu
27367          * @param {Roo.bootstrap.menu.Item} this
27368          * @param {Roo.EventObject} e
27369          */
27370         mouseout : true,
27371         // raw events
27372         /**
27373          * @event click
27374          * The raw click event for the entire grid.
27375          * @param {Roo.EventObject} e
27376          */
27377         click : true
27378     });
27379 };
27380
27381 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27382     
27383     submenu : false,
27384     href : '',
27385     html : '',
27386     preventDefault: true,
27387     disable : false,
27388     icon : false,
27389     pos : 'right',
27390     
27391     getAutoCreate : function()
27392     {
27393         var text = [
27394             {
27395                 tag : 'span',
27396                 cls : 'roo-menu-item-text',
27397                 html : this.html
27398             }
27399         ];
27400         
27401         if(this.icon){
27402             text.unshift({
27403                 tag : 'i',
27404                 cls : 'fa ' + this.icon
27405             })
27406         }
27407         
27408         var cfg = {
27409             tag : 'li',
27410             cn : [
27411                 {
27412                     tag : 'a',
27413                     href : this.href || '#',
27414                     cn : text
27415                 }
27416             ]
27417         };
27418         
27419         if(this.disable){
27420             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27421         }
27422         
27423         if(this.submenu){
27424             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27425             
27426             if(this.pos == 'left'){
27427                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27428             }
27429         }
27430         
27431         return cfg;
27432     },
27433     
27434     initEvents : function() 
27435     {
27436         this.el.on('mouseover', this.onMouseOver, this);
27437         this.el.on('mouseout', this.onMouseOut, this);
27438         
27439         this.el.select('a', true).first().on('click', this.onClick, this);
27440         
27441     },
27442     
27443     onClick : function(e)
27444     {
27445         if(this.preventDefault){
27446             e.preventDefault();
27447         }
27448         
27449         this.fireEvent("click", this, e);
27450     },
27451     
27452     onMouseOver : function(e)
27453     {
27454         if(this.submenu && this.pos == 'left'){
27455             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27456         }
27457         
27458         this.fireEvent("mouseover", this, e);
27459     },
27460     
27461     onMouseOut : function(e)
27462     {
27463         this.fireEvent("mouseout", this, e);
27464     }
27465 });
27466
27467  
27468
27469  /*
27470  * - LGPL
27471  *
27472  * menu separator
27473  * 
27474  */
27475 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27476
27477 /**
27478  * @class Roo.bootstrap.menu.Separator
27479  * @extends Roo.bootstrap.Component
27480  * Bootstrap Separator class
27481  * 
27482  * @constructor
27483  * Create a new Separator
27484  * @param {Object} config The config object
27485  */
27486
27487
27488 Roo.bootstrap.menu.Separator = function(config){
27489     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27490 };
27491
27492 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27493     
27494     getAutoCreate : function(){
27495         var cfg = {
27496             tag : 'li',
27497             cls: 'divider'
27498         };
27499         
27500         return cfg;
27501     }
27502    
27503 });
27504
27505  
27506
27507  /*
27508  * - LGPL
27509  *
27510  * Tooltip
27511  * 
27512  */
27513
27514 /**
27515  * @class Roo.bootstrap.Tooltip
27516  * Bootstrap Tooltip class
27517  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27518  * to determine which dom element triggers the tooltip.
27519  * 
27520  * It needs to add support for additional attributes like tooltip-position
27521  * 
27522  * @constructor
27523  * Create a new Toolti
27524  * @param {Object} config The config object
27525  */
27526
27527 Roo.bootstrap.Tooltip = function(config){
27528     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27529     
27530     this.alignment = Roo.bootstrap.Tooltip.alignment;
27531     
27532     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27533         this.alignment = config.alignment;
27534     }
27535     
27536 };
27537
27538 Roo.apply(Roo.bootstrap.Tooltip, {
27539     /**
27540      * @function init initialize tooltip monitoring.
27541      * @static
27542      */
27543     currentEl : false,
27544     currentTip : false,
27545     currentRegion : false,
27546     
27547     //  init : delay?
27548     
27549     init : function()
27550     {
27551         Roo.get(document).on('mouseover', this.enter ,this);
27552         Roo.get(document).on('mouseout', this.leave, this);
27553          
27554         
27555         this.currentTip = new Roo.bootstrap.Tooltip();
27556     },
27557     
27558     enter : function(ev)
27559     {
27560         var dom = ev.getTarget();
27561         
27562         //Roo.log(['enter',dom]);
27563         var el = Roo.fly(dom);
27564         if (this.currentEl) {
27565             //Roo.log(dom);
27566             //Roo.log(this.currentEl);
27567             //Roo.log(this.currentEl.contains(dom));
27568             if (this.currentEl == el) {
27569                 return;
27570             }
27571             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27572                 return;
27573             }
27574
27575         }
27576         
27577         if (this.currentTip.el) {
27578             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27579         }    
27580         //Roo.log(ev);
27581         
27582         if(!el || el.dom == document){
27583             return;
27584         }
27585         
27586         var bindEl = el;
27587         
27588         // you can not look for children, as if el is the body.. then everythign is the child..
27589         if (!el.attr('tooltip')) { //
27590             if (!el.select("[tooltip]").elements.length) {
27591                 return;
27592             }
27593             // is the mouse over this child...?
27594             bindEl = el.select("[tooltip]").first();
27595             var xy = ev.getXY();
27596             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27597                 //Roo.log("not in region.");
27598                 return;
27599             }
27600             //Roo.log("child element over..");
27601             
27602         }
27603         this.currentEl = bindEl;
27604         this.currentTip.bind(bindEl);
27605         this.currentRegion = Roo.lib.Region.getRegion(dom);
27606         this.currentTip.enter();
27607         
27608     },
27609     leave : function(ev)
27610     {
27611         var dom = ev.getTarget();
27612         //Roo.log(['leave',dom]);
27613         if (!this.currentEl) {
27614             return;
27615         }
27616         
27617         
27618         if (dom != this.currentEl.dom) {
27619             return;
27620         }
27621         var xy = ev.getXY();
27622         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27623             return;
27624         }
27625         // only activate leave if mouse cursor is outside... bounding box..
27626         
27627         
27628         
27629         
27630         if (this.currentTip) {
27631             this.currentTip.leave();
27632         }
27633         //Roo.log('clear currentEl');
27634         this.currentEl = false;
27635         
27636         
27637     },
27638     alignment : {
27639         'left' : ['r-l', [-2,0], 'right'],
27640         'right' : ['l-r', [2,0], 'left'],
27641         'bottom' : ['t-b', [0,2], 'top'],
27642         'top' : [ 'b-t', [0,-2], 'bottom']
27643     }
27644     
27645 });
27646
27647
27648 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27649     
27650     
27651     bindEl : false,
27652     
27653     delay : null, // can be { show : 300 , hide: 500}
27654     
27655     timeout : null,
27656     
27657     hoverState : null, //???
27658     
27659     placement : 'bottom', 
27660     
27661     alignment : false,
27662     
27663     getAutoCreate : function(){
27664     
27665         var cfg = {
27666            cls : 'tooltip',
27667            role : 'tooltip',
27668            cn : [
27669                 {
27670                     cls : 'tooltip-arrow'
27671                 },
27672                 {
27673                     cls : 'tooltip-inner'
27674                 }
27675            ]
27676         };
27677         
27678         return cfg;
27679     },
27680     bind : function(el)
27681     {
27682         this.bindEl = el;
27683     },
27684       
27685     
27686     enter : function () {
27687        
27688         if (this.timeout != null) {
27689             clearTimeout(this.timeout);
27690         }
27691         
27692         this.hoverState = 'in';
27693          //Roo.log("enter - show");
27694         if (!this.delay || !this.delay.show) {
27695             this.show();
27696             return;
27697         }
27698         var _t = this;
27699         this.timeout = setTimeout(function () {
27700             if (_t.hoverState == 'in') {
27701                 _t.show();
27702             }
27703         }, this.delay.show);
27704     },
27705     leave : function()
27706     {
27707         clearTimeout(this.timeout);
27708     
27709         this.hoverState = 'out';
27710          if (!this.delay || !this.delay.hide) {
27711             this.hide();
27712             return;
27713         }
27714        
27715         var _t = this;
27716         this.timeout = setTimeout(function () {
27717             //Roo.log("leave - timeout");
27718             
27719             if (_t.hoverState == 'out') {
27720                 _t.hide();
27721                 Roo.bootstrap.Tooltip.currentEl = false;
27722             }
27723         }, delay);
27724     },
27725     
27726     show : function (msg)
27727     {
27728         if (!this.el) {
27729             this.render(document.body);
27730         }
27731         // set content.
27732         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27733         
27734         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27735         
27736         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27737         
27738         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27739         
27740         var placement = typeof this.placement == 'function' ?
27741             this.placement.call(this, this.el, on_el) :
27742             this.placement;
27743             
27744         var autoToken = /\s?auto?\s?/i;
27745         var autoPlace = autoToken.test(placement);
27746         if (autoPlace) {
27747             placement = placement.replace(autoToken, '') || 'top';
27748         }
27749         
27750         //this.el.detach()
27751         //this.el.setXY([0,0]);
27752         this.el.show();
27753         //this.el.dom.style.display='block';
27754         
27755         //this.el.appendTo(on_el);
27756         
27757         var p = this.getPosition();
27758         var box = this.el.getBox();
27759         
27760         if (autoPlace) {
27761             // fixme..
27762         }
27763         
27764         var align = this.alignment[placement];
27765         
27766         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27767         
27768         if(placement == 'top' || placement == 'bottom'){
27769             if(xy[0] < 0){
27770                 placement = 'right';
27771             }
27772             
27773             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27774                 placement = 'left';
27775             }
27776             
27777             var scroll = Roo.select('body', true).first().getScroll();
27778             
27779             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27780                 placement = 'top';
27781             }
27782             
27783             align = this.alignment[placement];
27784         }
27785         
27786         this.el.alignTo(this.bindEl, align[0],align[1]);
27787         //var arrow = this.el.select('.arrow',true).first();
27788         //arrow.set(align[2], 
27789         
27790         this.el.addClass(placement);
27791         
27792         this.el.addClass('in fade');
27793         
27794         this.hoverState = null;
27795         
27796         if (this.el.hasClass('fade')) {
27797             // fade it?
27798         }
27799         
27800     },
27801     hide : function()
27802     {
27803          
27804         if (!this.el) {
27805             return;
27806         }
27807         //this.el.setXY([0,0]);
27808         this.el.removeClass('in');
27809         //this.el.hide();
27810         
27811     }
27812     
27813 });
27814  
27815
27816  /*
27817  * - LGPL
27818  *
27819  * Location Picker
27820  * 
27821  */
27822
27823 /**
27824  * @class Roo.bootstrap.LocationPicker
27825  * @extends Roo.bootstrap.Component
27826  * Bootstrap LocationPicker class
27827  * @cfg {Number} latitude Position when init default 0
27828  * @cfg {Number} longitude Position when init default 0
27829  * @cfg {Number} zoom default 15
27830  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27831  * @cfg {Boolean} mapTypeControl default false
27832  * @cfg {Boolean} disableDoubleClickZoom default false
27833  * @cfg {Boolean} scrollwheel default true
27834  * @cfg {Boolean} streetViewControl default false
27835  * @cfg {Number} radius default 0
27836  * @cfg {String} locationName
27837  * @cfg {Boolean} draggable default true
27838  * @cfg {Boolean} enableAutocomplete default false
27839  * @cfg {Boolean} enableReverseGeocode default true
27840  * @cfg {String} markerTitle
27841  * 
27842  * @constructor
27843  * Create a new LocationPicker
27844  * @param {Object} config The config object
27845  */
27846
27847
27848 Roo.bootstrap.LocationPicker = function(config){
27849     
27850     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27851     
27852     this.addEvents({
27853         /**
27854          * @event initial
27855          * Fires when the picker initialized.
27856          * @param {Roo.bootstrap.LocationPicker} this
27857          * @param {Google Location} location
27858          */
27859         initial : true,
27860         /**
27861          * @event positionchanged
27862          * Fires when the picker position changed.
27863          * @param {Roo.bootstrap.LocationPicker} this
27864          * @param {Google Location} location
27865          */
27866         positionchanged : true,
27867         /**
27868          * @event resize
27869          * Fires when the map resize.
27870          * @param {Roo.bootstrap.LocationPicker} this
27871          */
27872         resize : true,
27873         /**
27874          * @event show
27875          * Fires when the map show.
27876          * @param {Roo.bootstrap.LocationPicker} this
27877          */
27878         show : true,
27879         /**
27880          * @event hide
27881          * Fires when the map hide.
27882          * @param {Roo.bootstrap.LocationPicker} this
27883          */
27884         hide : true,
27885         /**
27886          * @event mapClick
27887          * Fires when click the map.
27888          * @param {Roo.bootstrap.LocationPicker} this
27889          * @param {Map event} e
27890          */
27891         mapClick : true,
27892         /**
27893          * @event mapRightClick
27894          * Fires when right click the map.
27895          * @param {Roo.bootstrap.LocationPicker} this
27896          * @param {Map event} e
27897          */
27898         mapRightClick : true,
27899         /**
27900          * @event markerClick
27901          * Fires when click the marker.
27902          * @param {Roo.bootstrap.LocationPicker} this
27903          * @param {Map event} e
27904          */
27905         markerClick : true,
27906         /**
27907          * @event markerRightClick
27908          * Fires when right click the marker.
27909          * @param {Roo.bootstrap.LocationPicker} this
27910          * @param {Map event} e
27911          */
27912         markerRightClick : true,
27913         /**
27914          * @event OverlayViewDraw
27915          * Fires when OverlayView Draw
27916          * @param {Roo.bootstrap.LocationPicker} this
27917          */
27918         OverlayViewDraw : true,
27919         /**
27920          * @event OverlayViewOnAdd
27921          * Fires when OverlayView Draw
27922          * @param {Roo.bootstrap.LocationPicker} this
27923          */
27924         OverlayViewOnAdd : true,
27925         /**
27926          * @event OverlayViewOnRemove
27927          * Fires when OverlayView Draw
27928          * @param {Roo.bootstrap.LocationPicker} this
27929          */
27930         OverlayViewOnRemove : true,
27931         /**
27932          * @event OverlayViewShow
27933          * Fires when OverlayView Draw
27934          * @param {Roo.bootstrap.LocationPicker} this
27935          * @param {Pixel} cpx
27936          */
27937         OverlayViewShow : true,
27938         /**
27939          * @event OverlayViewHide
27940          * Fires when OverlayView Draw
27941          * @param {Roo.bootstrap.LocationPicker} this
27942          */
27943         OverlayViewHide : true,
27944         /**
27945          * @event loadexception
27946          * Fires when load google lib failed.
27947          * @param {Roo.bootstrap.LocationPicker} this
27948          */
27949         loadexception : true
27950     });
27951         
27952 };
27953
27954 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27955     
27956     gMapContext: false,
27957     
27958     latitude: 0,
27959     longitude: 0,
27960     zoom: 15,
27961     mapTypeId: false,
27962     mapTypeControl: false,
27963     disableDoubleClickZoom: false,
27964     scrollwheel: true,
27965     streetViewControl: false,
27966     radius: 0,
27967     locationName: '',
27968     draggable: true,
27969     enableAutocomplete: false,
27970     enableReverseGeocode: true,
27971     markerTitle: '',
27972     
27973     getAutoCreate: function()
27974     {
27975
27976         var cfg = {
27977             tag: 'div',
27978             cls: 'roo-location-picker'
27979         };
27980         
27981         return cfg
27982     },
27983     
27984     initEvents: function(ct, position)
27985     {       
27986         if(!this.el.getWidth() || this.isApplied()){
27987             return;
27988         }
27989         
27990         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27991         
27992         this.initial();
27993     },
27994     
27995     initial: function()
27996     {
27997         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27998             this.fireEvent('loadexception', this);
27999             return;
28000         }
28001         
28002         if(!this.mapTypeId){
28003             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28004         }
28005         
28006         this.gMapContext = this.GMapContext();
28007         
28008         this.initOverlayView();
28009         
28010         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28011         
28012         var _this = this;
28013                 
28014         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28015             _this.setPosition(_this.gMapContext.marker.position);
28016         });
28017         
28018         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28019             _this.fireEvent('mapClick', this, event);
28020             
28021         });
28022
28023         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28024             _this.fireEvent('mapRightClick', this, event);
28025             
28026         });
28027         
28028         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28029             _this.fireEvent('markerClick', this, event);
28030             
28031         });
28032
28033         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28034             _this.fireEvent('markerRightClick', this, event);
28035             
28036         });
28037         
28038         this.setPosition(this.gMapContext.location);
28039         
28040         this.fireEvent('initial', this, this.gMapContext.location);
28041     },
28042     
28043     initOverlayView: function()
28044     {
28045         var _this = this;
28046         
28047         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28048             
28049             draw: function()
28050             {
28051                 _this.fireEvent('OverlayViewDraw', _this);
28052             },
28053             
28054             onAdd: function()
28055             {
28056                 _this.fireEvent('OverlayViewOnAdd', _this);
28057             },
28058             
28059             onRemove: function()
28060             {
28061                 _this.fireEvent('OverlayViewOnRemove', _this);
28062             },
28063             
28064             show: function(cpx)
28065             {
28066                 _this.fireEvent('OverlayViewShow', _this, cpx);
28067             },
28068             
28069             hide: function()
28070             {
28071                 _this.fireEvent('OverlayViewHide', _this);
28072             }
28073             
28074         });
28075     },
28076     
28077     fromLatLngToContainerPixel: function(event)
28078     {
28079         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28080     },
28081     
28082     isApplied: function() 
28083     {
28084         return this.getGmapContext() == false ? false : true;
28085     },
28086     
28087     getGmapContext: function() 
28088     {
28089         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28090     },
28091     
28092     GMapContext: function() 
28093     {
28094         var position = new google.maps.LatLng(this.latitude, this.longitude);
28095         
28096         var _map = new google.maps.Map(this.el.dom, {
28097             center: position,
28098             zoom: this.zoom,
28099             mapTypeId: this.mapTypeId,
28100             mapTypeControl: this.mapTypeControl,
28101             disableDoubleClickZoom: this.disableDoubleClickZoom,
28102             scrollwheel: this.scrollwheel,
28103             streetViewControl: this.streetViewControl,
28104             locationName: this.locationName,
28105             draggable: this.draggable,
28106             enableAutocomplete: this.enableAutocomplete,
28107             enableReverseGeocode: this.enableReverseGeocode
28108         });
28109         
28110         var _marker = new google.maps.Marker({
28111             position: position,
28112             map: _map,
28113             title: this.markerTitle,
28114             draggable: this.draggable
28115         });
28116         
28117         return {
28118             map: _map,
28119             marker: _marker,
28120             circle: null,
28121             location: position,
28122             radius: this.radius,
28123             locationName: this.locationName,
28124             addressComponents: {
28125                 formatted_address: null,
28126                 addressLine1: null,
28127                 addressLine2: null,
28128                 streetName: null,
28129                 streetNumber: null,
28130                 city: null,
28131                 district: null,
28132                 state: null,
28133                 stateOrProvince: null
28134             },
28135             settings: this,
28136             domContainer: this.el.dom,
28137             geodecoder: new google.maps.Geocoder()
28138         };
28139     },
28140     
28141     drawCircle: function(center, radius, options) 
28142     {
28143         if (this.gMapContext.circle != null) {
28144             this.gMapContext.circle.setMap(null);
28145         }
28146         if (radius > 0) {
28147             radius *= 1;
28148             options = Roo.apply({}, options, {
28149                 strokeColor: "#0000FF",
28150                 strokeOpacity: .35,
28151                 strokeWeight: 2,
28152                 fillColor: "#0000FF",
28153                 fillOpacity: .2
28154             });
28155             
28156             options.map = this.gMapContext.map;
28157             options.radius = radius;
28158             options.center = center;
28159             this.gMapContext.circle = new google.maps.Circle(options);
28160             return this.gMapContext.circle;
28161         }
28162         
28163         return null;
28164     },
28165     
28166     setPosition: function(location) 
28167     {
28168         this.gMapContext.location = location;
28169         this.gMapContext.marker.setPosition(location);
28170         this.gMapContext.map.panTo(location);
28171         this.drawCircle(location, this.gMapContext.radius, {});
28172         
28173         var _this = this;
28174         
28175         if (this.gMapContext.settings.enableReverseGeocode) {
28176             this.gMapContext.geodecoder.geocode({
28177                 latLng: this.gMapContext.location
28178             }, function(results, status) {
28179                 
28180                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28181                     _this.gMapContext.locationName = results[0].formatted_address;
28182                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28183                     
28184                     _this.fireEvent('positionchanged', this, location);
28185                 }
28186             });
28187             
28188             return;
28189         }
28190         
28191         this.fireEvent('positionchanged', this, location);
28192     },
28193     
28194     resize: function()
28195     {
28196         google.maps.event.trigger(this.gMapContext.map, "resize");
28197         
28198         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28199         
28200         this.fireEvent('resize', this);
28201     },
28202     
28203     setPositionByLatLng: function(latitude, longitude)
28204     {
28205         this.setPosition(new google.maps.LatLng(latitude, longitude));
28206     },
28207     
28208     getCurrentPosition: function() 
28209     {
28210         return {
28211             latitude: this.gMapContext.location.lat(),
28212             longitude: this.gMapContext.location.lng()
28213         };
28214     },
28215     
28216     getAddressName: function() 
28217     {
28218         return this.gMapContext.locationName;
28219     },
28220     
28221     getAddressComponents: function() 
28222     {
28223         return this.gMapContext.addressComponents;
28224     },
28225     
28226     address_component_from_google_geocode: function(address_components) 
28227     {
28228         var result = {};
28229         
28230         for (var i = 0; i < address_components.length; i++) {
28231             var component = address_components[i];
28232             if (component.types.indexOf("postal_code") >= 0) {
28233                 result.postalCode = component.short_name;
28234             } else if (component.types.indexOf("street_number") >= 0) {
28235                 result.streetNumber = component.short_name;
28236             } else if (component.types.indexOf("route") >= 0) {
28237                 result.streetName = component.short_name;
28238             } else if (component.types.indexOf("neighborhood") >= 0) {
28239                 result.city = component.short_name;
28240             } else if (component.types.indexOf("locality") >= 0) {
28241                 result.city = component.short_name;
28242             } else if (component.types.indexOf("sublocality") >= 0) {
28243                 result.district = component.short_name;
28244             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28245                 result.stateOrProvince = component.short_name;
28246             } else if (component.types.indexOf("country") >= 0) {
28247                 result.country = component.short_name;
28248             }
28249         }
28250         
28251         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28252         result.addressLine2 = "";
28253         return result;
28254     },
28255     
28256     setZoomLevel: function(zoom)
28257     {
28258         this.gMapContext.map.setZoom(zoom);
28259     },
28260     
28261     show: function()
28262     {
28263         if(!this.el){
28264             return;
28265         }
28266         
28267         this.el.show();
28268         
28269         this.resize();
28270         
28271         this.fireEvent('show', this);
28272     },
28273     
28274     hide: function()
28275     {
28276         if(!this.el){
28277             return;
28278         }
28279         
28280         this.el.hide();
28281         
28282         this.fireEvent('hide', this);
28283     }
28284     
28285 });
28286
28287 Roo.apply(Roo.bootstrap.LocationPicker, {
28288     
28289     OverlayView : function(map, options)
28290     {
28291         options = options || {};
28292         
28293         this.setMap(map);
28294     }
28295     
28296     
28297 });/**
28298  * @class Roo.bootstrap.Alert
28299  * @extends Roo.bootstrap.Component
28300  * Bootstrap Alert class - shows an alert area box
28301  * eg
28302  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28303   Enter a valid email address
28304 </div>
28305  * @licence LGPL
28306  * @cfg {String} title The title of alert
28307  * @cfg {String} html The content of alert
28308  * @cfg {String} weight (  success | info | warning | danger )
28309  * @cfg {String} faicon font-awesomeicon
28310  * 
28311  * @constructor
28312  * Create a new alert
28313  * @param {Object} config The config object
28314  */
28315
28316
28317 Roo.bootstrap.Alert = function(config){
28318     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28319     
28320 };
28321
28322 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28323     
28324     title: '',
28325     html: '',
28326     weight: false,
28327     faicon: false,
28328     
28329     getAutoCreate : function()
28330     {
28331         
28332         var cfg = {
28333             tag : 'div',
28334             cls : 'alert',
28335             cn : [
28336                 {
28337                     tag : 'i',
28338                     cls : 'roo-alert-icon'
28339                     
28340                 },
28341                 {
28342                     tag : 'b',
28343                     cls : 'roo-alert-title',
28344                     html : this.title
28345                 },
28346                 {
28347                     tag : 'span',
28348                     cls : 'roo-alert-text',
28349                     html : this.html
28350                 }
28351             ]
28352         };
28353         
28354         if(this.faicon){
28355             cfg.cn[0].cls += ' fa ' + this.faicon;
28356         }
28357         
28358         if(this.weight){
28359             cfg.cls += ' alert-' + this.weight;
28360         }
28361         
28362         return cfg;
28363     },
28364     
28365     initEvents: function() 
28366     {
28367         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28368     },
28369     
28370     setTitle : function(str)
28371     {
28372         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28373     },
28374     
28375     setText : function(str)
28376     {
28377         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28378     },
28379     
28380     setWeight : function(weight)
28381     {
28382         if(this.weight){
28383             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28384         }
28385         
28386         this.weight = weight;
28387         
28388         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28389     },
28390     
28391     setIcon : function(icon)
28392     {
28393         if(this.faicon){
28394             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28395         }
28396         
28397         this.faicon = icon;
28398         
28399         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28400     },
28401     
28402     hide: function() 
28403     {
28404         this.el.hide();   
28405     },
28406     
28407     show: function() 
28408     {  
28409         this.el.show();   
28410     }
28411     
28412 });
28413
28414  
28415 /*
28416 * Licence: LGPL
28417 */
28418
28419 /**
28420  * @class Roo.bootstrap.UploadCropbox
28421  * @extends Roo.bootstrap.Component
28422  * Bootstrap UploadCropbox class
28423  * @cfg {String} emptyText show when image has been loaded
28424  * @cfg {String} rotateNotify show when image too small to rotate
28425  * @cfg {Number} errorTimeout default 3000
28426  * @cfg {Number} minWidth default 300
28427  * @cfg {Number} minHeight default 300
28428  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28429  * @cfg {Boolean} isDocument (true|false) default false
28430  * @cfg {String} url action url
28431  * @cfg {String} paramName default 'imageUpload'
28432  * @cfg {String} method default POST
28433  * @cfg {Boolean} loadMask (true|false) default true
28434  * @cfg {Boolean} loadingText default 'Loading...'
28435  * 
28436  * @constructor
28437  * Create a new UploadCropbox
28438  * @param {Object} config The config object
28439  */
28440
28441 Roo.bootstrap.UploadCropbox = function(config){
28442     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28443     
28444     this.addEvents({
28445         /**
28446          * @event beforeselectfile
28447          * Fire before select file
28448          * @param {Roo.bootstrap.UploadCropbox} this
28449          */
28450         "beforeselectfile" : true,
28451         /**
28452          * @event initial
28453          * Fire after initEvent
28454          * @param {Roo.bootstrap.UploadCropbox} this
28455          */
28456         "initial" : true,
28457         /**
28458          * @event crop
28459          * Fire after initEvent
28460          * @param {Roo.bootstrap.UploadCropbox} this
28461          * @param {String} data
28462          */
28463         "crop" : true,
28464         /**
28465          * @event prepare
28466          * Fire when preparing the file data
28467          * @param {Roo.bootstrap.UploadCropbox} this
28468          * @param {Object} file
28469          */
28470         "prepare" : true,
28471         /**
28472          * @event exception
28473          * Fire when get exception
28474          * @param {Roo.bootstrap.UploadCropbox} this
28475          * @param {XMLHttpRequest} xhr
28476          */
28477         "exception" : true,
28478         /**
28479          * @event beforeloadcanvas
28480          * Fire before load the canvas
28481          * @param {Roo.bootstrap.UploadCropbox} this
28482          * @param {String} src
28483          */
28484         "beforeloadcanvas" : true,
28485         /**
28486          * @event trash
28487          * Fire when trash image
28488          * @param {Roo.bootstrap.UploadCropbox} this
28489          */
28490         "trash" : true,
28491         /**
28492          * @event download
28493          * Fire when download the image
28494          * @param {Roo.bootstrap.UploadCropbox} this
28495          */
28496         "download" : true,
28497         /**
28498          * @event footerbuttonclick
28499          * Fire when footerbuttonclick
28500          * @param {Roo.bootstrap.UploadCropbox} this
28501          * @param {String} type
28502          */
28503         "footerbuttonclick" : true,
28504         /**
28505          * @event resize
28506          * Fire when resize
28507          * @param {Roo.bootstrap.UploadCropbox} this
28508          */
28509         "resize" : true,
28510         /**
28511          * @event rotate
28512          * Fire when rotate the image
28513          * @param {Roo.bootstrap.UploadCropbox} this
28514          * @param {String} pos
28515          */
28516         "rotate" : true,
28517         /**
28518          * @event inspect
28519          * Fire when inspect the file
28520          * @param {Roo.bootstrap.UploadCropbox} this
28521          * @param {Object} file
28522          */
28523         "inspect" : true,
28524         /**
28525          * @event upload
28526          * Fire when xhr upload the file
28527          * @param {Roo.bootstrap.UploadCropbox} this
28528          * @param {Object} data
28529          */
28530         "upload" : true,
28531         /**
28532          * @event arrange
28533          * Fire when arrange the file data
28534          * @param {Roo.bootstrap.UploadCropbox} this
28535          * @param {Object} formData
28536          */
28537         "arrange" : true
28538     });
28539     
28540     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28541 };
28542
28543 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28544     
28545     emptyText : 'Click to upload image',
28546     rotateNotify : 'Image is too small to rotate',
28547     errorTimeout : 3000,
28548     scale : 0,
28549     baseScale : 1,
28550     rotate : 0,
28551     dragable : false,
28552     pinching : false,
28553     mouseX : 0,
28554     mouseY : 0,
28555     cropData : false,
28556     minWidth : 300,
28557     minHeight : 300,
28558     file : false,
28559     exif : {},
28560     baseRotate : 1,
28561     cropType : 'image/jpeg',
28562     buttons : false,
28563     canvasLoaded : false,
28564     isDocument : false,
28565     method : 'POST',
28566     paramName : 'imageUpload',
28567     loadMask : true,
28568     loadingText : 'Loading...',
28569     maskEl : false,
28570     
28571     getAutoCreate : function()
28572     {
28573         var cfg = {
28574             tag : 'div',
28575             cls : 'roo-upload-cropbox',
28576             cn : [
28577                 {
28578                     tag : 'input',
28579                     cls : 'roo-upload-cropbox-selector',
28580                     type : 'file'
28581                 },
28582                 {
28583                     tag : 'div',
28584                     cls : 'roo-upload-cropbox-body',
28585                     style : 'cursor:pointer',
28586                     cn : [
28587                         {
28588                             tag : 'div',
28589                             cls : 'roo-upload-cropbox-preview'
28590                         },
28591                         {
28592                             tag : 'div',
28593                             cls : 'roo-upload-cropbox-thumb'
28594                         },
28595                         {
28596                             tag : 'div',
28597                             cls : 'roo-upload-cropbox-empty-notify',
28598                             html : this.emptyText
28599                         },
28600                         {
28601                             tag : 'div',
28602                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28603                             html : this.rotateNotify
28604                         }
28605                     ]
28606                 },
28607                 {
28608                     tag : 'div',
28609                     cls : 'roo-upload-cropbox-footer',
28610                     cn : {
28611                         tag : 'div',
28612                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28613                         cn : []
28614                     }
28615                 }
28616             ]
28617         };
28618         
28619         return cfg;
28620     },
28621     
28622     onRender : function(ct, position)
28623     {
28624         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28625         
28626         if (this.buttons.length) {
28627             
28628             Roo.each(this.buttons, function(bb) {
28629                 
28630                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28631                 
28632                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28633                 
28634             }, this);
28635         }
28636         
28637         if(this.loadMask){
28638             this.maskEl = this.el;
28639         }
28640     },
28641     
28642     initEvents : function()
28643     {
28644         this.urlAPI = (window.createObjectURL && window) || 
28645                                 (window.URL && URL.revokeObjectURL && URL) || 
28646                                 (window.webkitURL && webkitURL);
28647                         
28648         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28649         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28650         
28651         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28652         this.selectorEl.hide();
28653         
28654         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28655         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28656         
28657         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28658         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28659         this.thumbEl.hide();
28660         
28661         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28662         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28663         
28664         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28665         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28666         this.errorEl.hide();
28667         
28668         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28669         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28670         this.footerEl.hide();
28671         
28672         this.setThumbBoxSize();
28673         
28674         this.bind();
28675         
28676         this.resize();
28677         
28678         this.fireEvent('initial', this);
28679     },
28680
28681     bind : function()
28682     {
28683         var _this = this;
28684         
28685         window.addEventListener("resize", function() { _this.resize(); } );
28686         
28687         this.bodyEl.on('click', this.beforeSelectFile, this);
28688         
28689         if(Roo.isTouch){
28690             this.bodyEl.on('touchstart', this.onTouchStart, this);
28691             this.bodyEl.on('touchmove', this.onTouchMove, this);
28692             this.bodyEl.on('touchend', this.onTouchEnd, this);
28693         }
28694         
28695         if(!Roo.isTouch){
28696             this.bodyEl.on('mousedown', this.onMouseDown, this);
28697             this.bodyEl.on('mousemove', this.onMouseMove, this);
28698             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28699             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28700             Roo.get(document).on('mouseup', this.onMouseUp, this);
28701         }
28702         
28703         this.selectorEl.on('change', this.onFileSelected, this);
28704     },
28705     
28706     reset : function()
28707     {    
28708         this.scale = 0;
28709         this.baseScale = 1;
28710         this.rotate = 0;
28711         this.baseRotate = 1;
28712         this.dragable = false;
28713         this.pinching = false;
28714         this.mouseX = 0;
28715         this.mouseY = 0;
28716         this.cropData = false;
28717         this.notifyEl.dom.innerHTML = this.emptyText;
28718         
28719         this.selectorEl.dom.value = '';
28720         
28721     },
28722     
28723     resize : function()
28724     {
28725         if(this.fireEvent('resize', this) != false){
28726             this.setThumbBoxPosition();
28727             this.setCanvasPosition();
28728         }
28729     },
28730     
28731     onFooterButtonClick : function(e, el, o, type)
28732     {
28733         switch (type) {
28734             case 'rotate-left' :
28735                 this.onRotateLeft(e);
28736                 break;
28737             case 'rotate-right' :
28738                 this.onRotateRight(e);
28739                 break;
28740             case 'picture' :
28741                 this.beforeSelectFile(e);
28742                 break;
28743             case 'trash' :
28744                 this.trash(e);
28745                 break;
28746             case 'crop' :
28747                 this.crop(e);
28748                 break;
28749             case 'download' :
28750                 this.download(e);
28751                 break;
28752             default :
28753                 break;
28754         }
28755         
28756         this.fireEvent('footerbuttonclick', this, type);
28757     },
28758     
28759     beforeSelectFile : function(e)
28760     {
28761         e.preventDefault();
28762         
28763         if(this.fireEvent('beforeselectfile', this) != false){
28764             this.selectorEl.dom.click();
28765         }
28766     },
28767     
28768     onFileSelected : function(e)
28769     {
28770         e.preventDefault();
28771         
28772         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28773             return;
28774         }
28775         
28776         var file = this.selectorEl.dom.files[0];
28777         
28778         if(this.fireEvent('inspect', this, file) != false){
28779             this.prepare(file);
28780         }
28781         
28782     },
28783     
28784     trash : function(e)
28785     {
28786         this.fireEvent('trash', this);
28787     },
28788     
28789     download : function(e)
28790     {
28791         this.fireEvent('download', this);
28792     },
28793     
28794     loadCanvas : function(src)
28795     {   
28796         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28797             
28798             this.reset();
28799             
28800             this.imageEl = document.createElement('img');
28801             
28802             var _this = this;
28803             
28804             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28805             
28806             this.imageEl.src = src;
28807         }
28808     },
28809     
28810     onLoadCanvas : function()
28811     {   
28812         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28813         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28814         
28815         this.bodyEl.un('click', this.beforeSelectFile, this);
28816         
28817         this.notifyEl.hide();
28818         this.thumbEl.show();
28819         this.footerEl.show();
28820         
28821         this.baseRotateLevel();
28822         
28823         if(this.isDocument){
28824             this.setThumbBoxSize();
28825         }
28826         
28827         this.setThumbBoxPosition();
28828         
28829         this.baseScaleLevel();
28830         
28831         this.draw();
28832         
28833         this.resize();
28834         
28835         this.canvasLoaded = true;
28836         
28837         if(this.loadMask){
28838             this.maskEl.unmask();
28839         }
28840         
28841     },
28842     
28843     setCanvasPosition : function()
28844     {   
28845         if(!this.canvasEl){
28846             return;
28847         }
28848         
28849         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28850         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28851         
28852         this.previewEl.setLeft(pw);
28853         this.previewEl.setTop(ph);
28854         
28855     },
28856     
28857     onMouseDown : function(e)
28858     {   
28859         e.stopEvent();
28860         
28861         this.dragable = true;
28862         this.pinching = false;
28863         
28864         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28865             this.dragable = false;
28866             return;
28867         }
28868         
28869         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28870         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28871         
28872     },
28873     
28874     onMouseMove : function(e)
28875     {   
28876         e.stopEvent();
28877         
28878         if(!this.canvasLoaded){
28879             return;
28880         }
28881         
28882         if (!this.dragable){
28883             return;
28884         }
28885         
28886         var minX = Math.ceil(this.thumbEl.getLeft(true));
28887         var minY = Math.ceil(this.thumbEl.getTop(true));
28888         
28889         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28890         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28891         
28892         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28893         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28894         
28895         x = x - this.mouseX;
28896         y = y - this.mouseY;
28897         
28898         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28899         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28900         
28901         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28902         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28903         
28904         this.previewEl.setLeft(bgX);
28905         this.previewEl.setTop(bgY);
28906         
28907         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28908         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28909     },
28910     
28911     onMouseUp : function(e)
28912     {   
28913         e.stopEvent();
28914         
28915         this.dragable = false;
28916     },
28917     
28918     onMouseWheel : function(e)
28919     {   
28920         e.stopEvent();
28921         
28922         this.startScale = this.scale;
28923         
28924         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28925         
28926         if(!this.zoomable()){
28927             this.scale = this.startScale;
28928             return;
28929         }
28930         
28931         this.draw();
28932         
28933         return;
28934     },
28935     
28936     zoomable : function()
28937     {
28938         var minScale = this.thumbEl.getWidth() / this.minWidth;
28939         
28940         if(this.minWidth < this.minHeight){
28941             minScale = this.thumbEl.getHeight() / this.minHeight;
28942         }
28943         
28944         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28945         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28946         
28947         if(
28948                 this.isDocument &&
28949                 (this.rotate == 0 || this.rotate == 180) && 
28950                 (
28951                     width > this.imageEl.OriginWidth || 
28952                     height > this.imageEl.OriginHeight ||
28953                     (width < this.minWidth && height < this.minHeight)
28954                 )
28955         ){
28956             return false;
28957         }
28958         
28959         if(
28960                 this.isDocument &&
28961                 (this.rotate == 90 || this.rotate == 270) && 
28962                 (
28963                     width > this.imageEl.OriginWidth || 
28964                     height > this.imageEl.OriginHeight ||
28965                     (width < this.minHeight && height < this.minWidth)
28966                 )
28967         ){
28968             return false;
28969         }
28970         
28971         if(
28972                 !this.isDocument &&
28973                 (this.rotate == 0 || this.rotate == 180) && 
28974                 (
28975                     width < this.minWidth || 
28976                     width > this.imageEl.OriginWidth || 
28977                     height < this.minHeight || 
28978                     height > this.imageEl.OriginHeight
28979                 )
28980         ){
28981             return false;
28982         }
28983         
28984         if(
28985                 !this.isDocument &&
28986                 (this.rotate == 90 || this.rotate == 270) && 
28987                 (
28988                     width < this.minHeight || 
28989                     width > this.imageEl.OriginWidth || 
28990                     height < this.minWidth || 
28991                     height > this.imageEl.OriginHeight
28992                 )
28993         ){
28994             return false;
28995         }
28996         
28997         return true;
28998         
28999     },
29000     
29001     onRotateLeft : function(e)
29002     {   
29003         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29004             
29005             var minScale = this.thumbEl.getWidth() / this.minWidth;
29006             
29007             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29008             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29009             
29010             this.startScale = this.scale;
29011             
29012             while (this.getScaleLevel() < minScale){
29013             
29014                 this.scale = this.scale + 1;
29015                 
29016                 if(!this.zoomable()){
29017                     break;
29018                 }
29019                 
29020                 if(
29021                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29022                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29023                 ){
29024                     continue;
29025                 }
29026                 
29027                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29028
29029                 this.draw();
29030                 
29031                 return;
29032             }
29033             
29034             this.scale = this.startScale;
29035             
29036             this.onRotateFail();
29037             
29038             return false;
29039         }
29040         
29041         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29042
29043         if(this.isDocument){
29044             this.setThumbBoxSize();
29045             this.setThumbBoxPosition();
29046             this.setCanvasPosition();
29047         }
29048         
29049         this.draw();
29050         
29051         this.fireEvent('rotate', this, 'left');
29052         
29053     },
29054     
29055     onRotateRight : function(e)
29056     {
29057         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29058             
29059             var minScale = this.thumbEl.getWidth() / this.minWidth;
29060         
29061             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29062             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29063             
29064             this.startScale = this.scale;
29065             
29066             while (this.getScaleLevel() < minScale){
29067             
29068                 this.scale = this.scale + 1;
29069                 
29070                 if(!this.zoomable()){
29071                     break;
29072                 }
29073                 
29074                 if(
29075                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29076                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29077                 ){
29078                     continue;
29079                 }
29080                 
29081                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29082
29083                 this.draw();
29084                 
29085                 return;
29086             }
29087             
29088             this.scale = this.startScale;
29089             
29090             this.onRotateFail();
29091             
29092             return false;
29093         }
29094         
29095         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29096
29097         if(this.isDocument){
29098             this.setThumbBoxSize();
29099             this.setThumbBoxPosition();
29100             this.setCanvasPosition();
29101         }
29102         
29103         this.draw();
29104         
29105         this.fireEvent('rotate', this, 'right');
29106     },
29107     
29108     onRotateFail : function()
29109     {
29110         this.errorEl.show(true);
29111         
29112         var _this = this;
29113         
29114         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29115     },
29116     
29117     draw : function()
29118     {
29119         this.previewEl.dom.innerHTML = '';
29120         
29121         var canvasEl = document.createElement("canvas");
29122         
29123         var contextEl = canvasEl.getContext("2d");
29124         
29125         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29126         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29127         var center = this.imageEl.OriginWidth / 2;
29128         
29129         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29130             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29131             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29132             center = this.imageEl.OriginHeight / 2;
29133         }
29134         
29135         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29136         
29137         contextEl.translate(center, center);
29138         contextEl.rotate(this.rotate * Math.PI / 180);
29139
29140         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29141         
29142         this.canvasEl = document.createElement("canvas");
29143         
29144         this.contextEl = this.canvasEl.getContext("2d");
29145         
29146         switch (this.rotate) {
29147             case 0 :
29148                 
29149                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29150                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29151                 
29152                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29153                 
29154                 break;
29155             case 90 : 
29156                 
29157                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29158                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29159                 
29160                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29161                     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);
29162                     break;
29163                 }
29164                 
29165                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29166                 
29167                 break;
29168             case 180 :
29169                 
29170                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29171                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29172                 
29173                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29174                     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);
29175                     break;
29176                 }
29177                 
29178                 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);
29179                 
29180                 break;
29181             case 270 :
29182                 
29183                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29184                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29185         
29186                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29187                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29188                     break;
29189                 }
29190                 
29191                 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);
29192                 
29193                 break;
29194             default : 
29195                 break;
29196         }
29197         
29198         this.previewEl.appendChild(this.canvasEl);
29199         
29200         this.setCanvasPosition();
29201     },
29202     
29203     crop : function()
29204     {
29205         if(!this.canvasLoaded){
29206             return;
29207         }
29208         
29209         var imageCanvas = document.createElement("canvas");
29210         
29211         var imageContext = imageCanvas.getContext("2d");
29212         
29213         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29214         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29215         
29216         var center = imageCanvas.width / 2;
29217         
29218         imageContext.translate(center, center);
29219         
29220         imageContext.rotate(this.rotate * Math.PI / 180);
29221         
29222         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29223         
29224         var canvas = document.createElement("canvas");
29225         
29226         var context = canvas.getContext("2d");
29227                 
29228         canvas.width = this.minWidth;
29229         canvas.height = this.minHeight;
29230
29231         switch (this.rotate) {
29232             case 0 :
29233                 
29234                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29235                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29236                 
29237                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29238                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29239                 
29240                 var targetWidth = this.minWidth - 2 * x;
29241                 var targetHeight = this.minHeight - 2 * y;
29242                 
29243                 var scale = 1;
29244                 
29245                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29246                     scale = targetWidth / width;
29247                 }
29248                 
29249                 if(x > 0 && y == 0){
29250                     scale = targetHeight / height;
29251                 }
29252                 
29253                 if(x > 0 && y > 0){
29254                     scale = targetWidth / width;
29255                     
29256                     if(width < height){
29257                         scale = targetHeight / height;
29258                     }
29259                 }
29260                 
29261                 context.scale(scale, scale);
29262                 
29263                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29264                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29265
29266                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29267                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29268
29269                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29270                 
29271                 break;
29272             case 90 : 
29273                 
29274                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29275                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29276                 
29277                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29278                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29279                 
29280                 var targetWidth = this.minWidth - 2 * x;
29281                 var targetHeight = this.minHeight - 2 * y;
29282                 
29283                 var scale = 1;
29284                 
29285                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29286                     scale = targetWidth / width;
29287                 }
29288                 
29289                 if(x > 0 && y == 0){
29290                     scale = targetHeight / height;
29291                 }
29292                 
29293                 if(x > 0 && y > 0){
29294                     scale = targetWidth / width;
29295                     
29296                     if(width < height){
29297                         scale = targetHeight / height;
29298                     }
29299                 }
29300                 
29301                 context.scale(scale, scale);
29302                 
29303                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29304                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29305
29306                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29307                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29308                 
29309                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29310                 
29311                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29312                 
29313                 break;
29314             case 180 :
29315                 
29316                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29317                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29318                 
29319                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29320                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29321                 
29322                 var targetWidth = this.minWidth - 2 * x;
29323                 var targetHeight = this.minHeight - 2 * y;
29324                 
29325                 var scale = 1;
29326                 
29327                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29328                     scale = targetWidth / width;
29329                 }
29330                 
29331                 if(x > 0 && y == 0){
29332                     scale = targetHeight / height;
29333                 }
29334                 
29335                 if(x > 0 && y > 0){
29336                     scale = targetWidth / width;
29337                     
29338                     if(width < height){
29339                         scale = targetHeight / height;
29340                     }
29341                 }
29342                 
29343                 context.scale(scale, scale);
29344                 
29345                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29346                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29347
29348                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29349                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29350
29351                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29352                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29353                 
29354                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29355                 
29356                 break;
29357             case 270 :
29358                 
29359                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29360                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29361                 
29362                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29363                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29364                 
29365                 var targetWidth = this.minWidth - 2 * x;
29366                 var targetHeight = this.minHeight - 2 * y;
29367                 
29368                 var scale = 1;
29369                 
29370                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29371                     scale = targetWidth / width;
29372                 }
29373                 
29374                 if(x > 0 && y == 0){
29375                     scale = targetHeight / height;
29376                 }
29377                 
29378                 if(x > 0 && y > 0){
29379                     scale = targetWidth / width;
29380                     
29381                     if(width < height){
29382                         scale = targetHeight / height;
29383                     }
29384                 }
29385                 
29386                 context.scale(scale, scale);
29387                 
29388                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29389                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29390
29391                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29392                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29393                 
29394                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29395                 
29396                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29397                 
29398                 break;
29399             default : 
29400                 break;
29401         }
29402         
29403         this.cropData = canvas.toDataURL(this.cropType);
29404         
29405         if(this.fireEvent('crop', this, this.cropData) !== false){
29406             this.process(this.file, this.cropData);
29407         }
29408         
29409         return;
29410         
29411     },
29412     
29413     setThumbBoxSize : function()
29414     {
29415         var width, height;
29416         
29417         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29418             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29419             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29420             
29421             this.minWidth = width;
29422             this.minHeight = height;
29423             
29424             if(this.rotate == 90 || this.rotate == 270){
29425                 this.minWidth = height;
29426                 this.minHeight = width;
29427             }
29428         }
29429         
29430         height = 300;
29431         width = Math.ceil(this.minWidth * height / this.minHeight);
29432         
29433         if(this.minWidth > this.minHeight){
29434             width = 300;
29435             height = Math.ceil(this.minHeight * width / this.minWidth);
29436         }
29437         
29438         this.thumbEl.setStyle({
29439             width : width + 'px',
29440             height : height + 'px'
29441         });
29442
29443         return;
29444             
29445     },
29446     
29447     setThumbBoxPosition : function()
29448     {
29449         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29450         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29451         
29452         this.thumbEl.setLeft(x);
29453         this.thumbEl.setTop(y);
29454         
29455     },
29456     
29457     baseRotateLevel : function()
29458     {
29459         this.baseRotate = 1;
29460         
29461         if(
29462                 typeof(this.exif) != 'undefined' &&
29463                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29464                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29465         ){
29466             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29467         }
29468         
29469         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29470         
29471     },
29472     
29473     baseScaleLevel : function()
29474     {
29475         var width, height;
29476         
29477         if(this.isDocument){
29478             
29479             if(this.baseRotate == 6 || this.baseRotate == 8){
29480             
29481                 height = this.thumbEl.getHeight();
29482                 this.baseScale = height / this.imageEl.OriginWidth;
29483
29484                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29485                     width = this.thumbEl.getWidth();
29486                     this.baseScale = width / this.imageEl.OriginHeight;
29487                 }
29488
29489                 return;
29490             }
29491
29492             height = this.thumbEl.getHeight();
29493             this.baseScale = height / this.imageEl.OriginHeight;
29494
29495             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29496                 width = this.thumbEl.getWidth();
29497                 this.baseScale = width / this.imageEl.OriginWidth;
29498             }
29499
29500             return;
29501         }
29502         
29503         if(this.baseRotate == 6 || this.baseRotate == 8){
29504             
29505             width = this.thumbEl.getHeight();
29506             this.baseScale = width / this.imageEl.OriginHeight;
29507             
29508             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29509                 height = this.thumbEl.getWidth();
29510                 this.baseScale = height / this.imageEl.OriginHeight;
29511             }
29512             
29513             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29514                 height = this.thumbEl.getWidth();
29515                 this.baseScale = height / this.imageEl.OriginHeight;
29516                 
29517                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29518                     width = this.thumbEl.getHeight();
29519                     this.baseScale = width / this.imageEl.OriginWidth;
29520                 }
29521             }
29522             
29523             return;
29524         }
29525         
29526         width = this.thumbEl.getWidth();
29527         this.baseScale = width / this.imageEl.OriginWidth;
29528         
29529         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29530             height = this.thumbEl.getHeight();
29531             this.baseScale = height / this.imageEl.OriginHeight;
29532         }
29533         
29534         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29535             
29536             height = this.thumbEl.getHeight();
29537             this.baseScale = height / this.imageEl.OriginHeight;
29538             
29539             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29540                 width = this.thumbEl.getWidth();
29541                 this.baseScale = width / this.imageEl.OriginWidth;
29542             }
29543             
29544         }
29545         
29546         return;
29547     },
29548     
29549     getScaleLevel : function()
29550     {
29551         return this.baseScale * Math.pow(1.1, this.scale);
29552     },
29553     
29554     onTouchStart : function(e)
29555     {
29556         if(!this.canvasLoaded){
29557             this.beforeSelectFile(e);
29558             return;
29559         }
29560         
29561         var touches = e.browserEvent.touches;
29562         
29563         if(!touches){
29564             return;
29565         }
29566         
29567         if(touches.length == 1){
29568             this.onMouseDown(e);
29569             return;
29570         }
29571         
29572         if(touches.length != 2){
29573             return;
29574         }
29575         
29576         var coords = [];
29577         
29578         for(var i = 0, finger; finger = touches[i]; i++){
29579             coords.push(finger.pageX, finger.pageY);
29580         }
29581         
29582         var x = Math.pow(coords[0] - coords[2], 2);
29583         var y = Math.pow(coords[1] - coords[3], 2);
29584         
29585         this.startDistance = Math.sqrt(x + y);
29586         
29587         this.startScale = this.scale;
29588         
29589         this.pinching = true;
29590         this.dragable = false;
29591         
29592     },
29593     
29594     onTouchMove : function(e)
29595     {
29596         if(!this.pinching && !this.dragable){
29597             return;
29598         }
29599         
29600         var touches = e.browserEvent.touches;
29601         
29602         if(!touches){
29603             return;
29604         }
29605         
29606         if(this.dragable){
29607             this.onMouseMove(e);
29608             return;
29609         }
29610         
29611         var coords = [];
29612         
29613         for(var i = 0, finger; finger = touches[i]; i++){
29614             coords.push(finger.pageX, finger.pageY);
29615         }
29616         
29617         var x = Math.pow(coords[0] - coords[2], 2);
29618         var y = Math.pow(coords[1] - coords[3], 2);
29619         
29620         this.endDistance = Math.sqrt(x + y);
29621         
29622         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29623         
29624         if(!this.zoomable()){
29625             this.scale = this.startScale;
29626             return;
29627         }
29628         
29629         this.draw();
29630         
29631     },
29632     
29633     onTouchEnd : function(e)
29634     {
29635         this.pinching = false;
29636         this.dragable = false;
29637         
29638     },
29639     
29640     process : function(file, crop)
29641     {
29642         if(this.loadMask){
29643             this.maskEl.mask(this.loadingText);
29644         }
29645         
29646         this.xhr = new XMLHttpRequest();
29647         
29648         file.xhr = this.xhr;
29649
29650         this.xhr.open(this.method, this.url, true);
29651         
29652         var headers = {
29653             "Accept": "application/json",
29654             "Cache-Control": "no-cache",
29655             "X-Requested-With": "XMLHttpRequest"
29656         };
29657         
29658         for (var headerName in headers) {
29659             var headerValue = headers[headerName];
29660             if (headerValue) {
29661                 this.xhr.setRequestHeader(headerName, headerValue);
29662             }
29663         }
29664         
29665         var _this = this;
29666         
29667         this.xhr.onload = function()
29668         {
29669             _this.xhrOnLoad(_this.xhr);
29670         }
29671         
29672         this.xhr.onerror = function()
29673         {
29674             _this.xhrOnError(_this.xhr);
29675         }
29676         
29677         var formData = new FormData();
29678
29679         formData.append('returnHTML', 'NO');
29680         
29681         if(crop){
29682             formData.append('crop', crop);
29683         }
29684         
29685         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29686             formData.append(this.paramName, file, file.name);
29687         }
29688         
29689         if(typeof(file.filename) != 'undefined'){
29690             formData.append('filename', file.filename);
29691         }
29692         
29693         if(typeof(file.mimetype) != 'undefined'){
29694             formData.append('mimetype', file.mimetype);
29695         }
29696         
29697         if(this.fireEvent('arrange', this, formData) != false){
29698             this.xhr.send(formData);
29699         };
29700     },
29701     
29702     xhrOnLoad : function(xhr)
29703     {
29704         if(this.loadMask){
29705             this.maskEl.unmask();
29706         }
29707         
29708         if (xhr.readyState !== 4) {
29709             this.fireEvent('exception', this, xhr);
29710             return;
29711         }
29712
29713         var response = Roo.decode(xhr.responseText);
29714         
29715         if(!response.success){
29716             this.fireEvent('exception', this, xhr);
29717             return;
29718         }
29719         
29720         var response = Roo.decode(xhr.responseText);
29721         
29722         this.fireEvent('upload', this, response);
29723         
29724     },
29725     
29726     xhrOnError : function()
29727     {
29728         if(this.loadMask){
29729             this.maskEl.unmask();
29730         }
29731         
29732         Roo.log('xhr on error');
29733         
29734         var response = Roo.decode(xhr.responseText);
29735           
29736         Roo.log(response);
29737         
29738     },
29739     
29740     prepare : function(file)
29741     {   
29742         if(this.loadMask){
29743             this.maskEl.mask(this.loadingText);
29744         }
29745         
29746         this.file = false;
29747         this.exif = {};
29748         
29749         if(typeof(file) === 'string'){
29750             this.loadCanvas(file);
29751             return;
29752         }
29753         
29754         if(!file || !this.urlAPI){
29755             return;
29756         }
29757         
29758         this.file = file;
29759         this.cropType = file.type;
29760         
29761         var _this = this;
29762         
29763         if(this.fireEvent('prepare', this, this.file) != false){
29764             
29765             var reader = new FileReader();
29766             
29767             reader.onload = function (e) {
29768                 if (e.target.error) {
29769                     Roo.log(e.target.error);
29770                     return;
29771                 }
29772                 
29773                 var buffer = e.target.result,
29774                     dataView = new DataView(buffer),
29775                     offset = 2,
29776                     maxOffset = dataView.byteLength - 4,
29777                     markerBytes,
29778                     markerLength;
29779                 
29780                 if (dataView.getUint16(0) === 0xffd8) {
29781                     while (offset < maxOffset) {
29782                         markerBytes = dataView.getUint16(offset);
29783                         
29784                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29785                             markerLength = dataView.getUint16(offset + 2) + 2;
29786                             if (offset + markerLength > dataView.byteLength) {
29787                                 Roo.log('Invalid meta data: Invalid segment size.');
29788                                 break;
29789                             }
29790                             
29791                             if(markerBytes == 0xffe1){
29792                                 _this.parseExifData(
29793                                     dataView,
29794                                     offset,
29795                                     markerLength
29796                                 );
29797                             }
29798                             
29799                             offset += markerLength;
29800                             
29801                             continue;
29802                         }
29803                         
29804                         break;
29805                     }
29806                     
29807                 }
29808                 
29809                 var url = _this.urlAPI.createObjectURL(_this.file);
29810                 
29811                 _this.loadCanvas(url);
29812                 
29813                 return;
29814             }
29815             
29816             reader.readAsArrayBuffer(this.file);
29817             
29818         }
29819         
29820     },
29821     
29822     parseExifData : function(dataView, offset, length)
29823     {
29824         var tiffOffset = offset + 10,
29825             littleEndian,
29826             dirOffset;
29827     
29828         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29829             // No Exif data, might be XMP data instead
29830             return;
29831         }
29832         
29833         // Check for the ASCII code for "Exif" (0x45786966):
29834         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29835             // No Exif data, might be XMP data instead
29836             return;
29837         }
29838         if (tiffOffset + 8 > dataView.byteLength) {
29839             Roo.log('Invalid Exif data: Invalid segment size.');
29840             return;
29841         }
29842         // Check for the two null bytes:
29843         if (dataView.getUint16(offset + 8) !== 0x0000) {
29844             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29845             return;
29846         }
29847         // Check the byte alignment:
29848         switch (dataView.getUint16(tiffOffset)) {
29849         case 0x4949:
29850             littleEndian = true;
29851             break;
29852         case 0x4D4D:
29853             littleEndian = false;
29854             break;
29855         default:
29856             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29857             return;
29858         }
29859         // Check for the TIFF tag marker (0x002A):
29860         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29861             Roo.log('Invalid Exif data: Missing TIFF marker.');
29862             return;
29863         }
29864         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29865         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29866         
29867         this.parseExifTags(
29868             dataView,
29869             tiffOffset,
29870             tiffOffset + dirOffset,
29871             littleEndian
29872         );
29873     },
29874     
29875     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29876     {
29877         var tagsNumber,
29878             dirEndOffset,
29879             i;
29880         if (dirOffset + 6 > dataView.byteLength) {
29881             Roo.log('Invalid Exif data: Invalid directory offset.');
29882             return;
29883         }
29884         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29885         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29886         if (dirEndOffset + 4 > dataView.byteLength) {
29887             Roo.log('Invalid Exif data: Invalid directory size.');
29888             return;
29889         }
29890         for (i = 0; i < tagsNumber; i += 1) {
29891             this.parseExifTag(
29892                 dataView,
29893                 tiffOffset,
29894                 dirOffset + 2 + 12 * i, // tag offset
29895                 littleEndian
29896             );
29897         }
29898         // Return the offset to the next directory:
29899         return dataView.getUint32(dirEndOffset, littleEndian);
29900     },
29901     
29902     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29903     {
29904         var tag = dataView.getUint16(offset, littleEndian);
29905         
29906         this.exif[tag] = this.getExifValue(
29907             dataView,
29908             tiffOffset,
29909             offset,
29910             dataView.getUint16(offset + 2, littleEndian), // tag type
29911             dataView.getUint32(offset + 4, littleEndian), // tag length
29912             littleEndian
29913         );
29914     },
29915     
29916     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29917     {
29918         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29919             tagSize,
29920             dataOffset,
29921             values,
29922             i,
29923             str,
29924             c;
29925     
29926         if (!tagType) {
29927             Roo.log('Invalid Exif data: Invalid tag type.');
29928             return;
29929         }
29930         
29931         tagSize = tagType.size * length;
29932         // Determine if the value is contained in the dataOffset bytes,
29933         // or if the value at the dataOffset is a pointer to the actual data:
29934         dataOffset = tagSize > 4 ?
29935                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29936         if (dataOffset + tagSize > dataView.byteLength) {
29937             Roo.log('Invalid Exif data: Invalid data offset.');
29938             return;
29939         }
29940         if (length === 1) {
29941             return tagType.getValue(dataView, dataOffset, littleEndian);
29942         }
29943         values = [];
29944         for (i = 0; i < length; i += 1) {
29945             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29946         }
29947         
29948         if (tagType.ascii) {
29949             str = '';
29950             // Concatenate the chars:
29951             for (i = 0; i < values.length; i += 1) {
29952                 c = values[i];
29953                 // Ignore the terminating NULL byte(s):
29954                 if (c === '\u0000') {
29955                     break;
29956                 }
29957                 str += c;
29958             }
29959             return str;
29960         }
29961         return values;
29962     }
29963     
29964 });
29965
29966 Roo.apply(Roo.bootstrap.UploadCropbox, {
29967     tags : {
29968         'Orientation': 0x0112
29969     },
29970     
29971     Orientation: {
29972             1: 0, //'top-left',
29973 //            2: 'top-right',
29974             3: 180, //'bottom-right',
29975 //            4: 'bottom-left',
29976 //            5: 'left-top',
29977             6: 90, //'right-top',
29978 //            7: 'right-bottom',
29979             8: 270 //'left-bottom'
29980     },
29981     
29982     exifTagTypes : {
29983         // byte, 8-bit unsigned int:
29984         1: {
29985             getValue: function (dataView, dataOffset) {
29986                 return dataView.getUint8(dataOffset);
29987             },
29988             size: 1
29989         },
29990         // ascii, 8-bit byte:
29991         2: {
29992             getValue: function (dataView, dataOffset) {
29993                 return String.fromCharCode(dataView.getUint8(dataOffset));
29994             },
29995             size: 1,
29996             ascii: true
29997         },
29998         // short, 16 bit int:
29999         3: {
30000             getValue: function (dataView, dataOffset, littleEndian) {
30001                 return dataView.getUint16(dataOffset, littleEndian);
30002             },
30003             size: 2
30004         },
30005         // long, 32 bit int:
30006         4: {
30007             getValue: function (dataView, dataOffset, littleEndian) {
30008                 return dataView.getUint32(dataOffset, littleEndian);
30009             },
30010             size: 4
30011         },
30012         // rational = two long values, first is numerator, second is denominator:
30013         5: {
30014             getValue: function (dataView, dataOffset, littleEndian) {
30015                 return dataView.getUint32(dataOffset, littleEndian) /
30016                     dataView.getUint32(dataOffset + 4, littleEndian);
30017             },
30018             size: 8
30019         },
30020         // slong, 32 bit signed int:
30021         9: {
30022             getValue: function (dataView, dataOffset, littleEndian) {
30023                 return dataView.getInt32(dataOffset, littleEndian);
30024             },
30025             size: 4
30026         },
30027         // srational, two slongs, first is numerator, second is denominator:
30028         10: {
30029             getValue: function (dataView, dataOffset, littleEndian) {
30030                 return dataView.getInt32(dataOffset, littleEndian) /
30031                     dataView.getInt32(dataOffset + 4, littleEndian);
30032             },
30033             size: 8
30034         }
30035     },
30036     
30037     footer : {
30038         STANDARD : [
30039             {
30040                 tag : 'div',
30041                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30042                 action : 'rotate-left',
30043                 cn : [
30044                     {
30045                         tag : 'button',
30046                         cls : 'btn btn-default',
30047                         html : '<i class="fa fa-undo"></i>'
30048                     }
30049                 ]
30050             },
30051             {
30052                 tag : 'div',
30053                 cls : 'btn-group roo-upload-cropbox-picture',
30054                 action : 'picture',
30055                 cn : [
30056                     {
30057                         tag : 'button',
30058                         cls : 'btn btn-default',
30059                         html : '<i class="fa fa-picture-o"></i>'
30060                     }
30061                 ]
30062             },
30063             {
30064                 tag : 'div',
30065                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30066                 action : 'rotate-right',
30067                 cn : [
30068                     {
30069                         tag : 'button',
30070                         cls : 'btn btn-default',
30071                         html : '<i class="fa fa-repeat"></i>'
30072                     }
30073                 ]
30074             }
30075         ],
30076         DOCUMENT : [
30077             {
30078                 tag : 'div',
30079                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30080                 action : 'rotate-left',
30081                 cn : [
30082                     {
30083                         tag : 'button',
30084                         cls : 'btn btn-default',
30085                         html : '<i class="fa fa-undo"></i>'
30086                     }
30087                 ]
30088             },
30089             {
30090                 tag : 'div',
30091                 cls : 'btn-group roo-upload-cropbox-download',
30092                 action : 'download',
30093                 cn : [
30094                     {
30095                         tag : 'button',
30096                         cls : 'btn btn-default',
30097                         html : '<i class="fa fa-download"></i>'
30098                     }
30099                 ]
30100             },
30101             {
30102                 tag : 'div',
30103                 cls : 'btn-group roo-upload-cropbox-crop',
30104                 action : 'crop',
30105                 cn : [
30106                     {
30107                         tag : 'button',
30108                         cls : 'btn btn-default',
30109                         html : '<i class="fa fa-crop"></i>'
30110                     }
30111                 ]
30112             },
30113             {
30114                 tag : 'div',
30115                 cls : 'btn-group roo-upload-cropbox-trash',
30116                 action : 'trash',
30117                 cn : [
30118                     {
30119                         tag : 'button',
30120                         cls : 'btn btn-default',
30121                         html : '<i class="fa fa-trash"></i>'
30122                     }
30123                 ]
30124             },
30125             {
30126                 tag : 'div',
30127                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30128                 action : 'rotate-right',
30129                 cn : [
30130                     {
30131                         tag : 'button',
30132                         cls : 'btn btn-default',
30133                         html : '<i class="fa fa-repeat"></i>'
30134                     }
30135                 ]
30136             }
30137         ],
30138         ROTATOR : [
30139             {
30140                 tag : 'div',
30141                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30142                 action : 'rotate-left',
30143                 cn : [
30144                     {
30145                         tag : 'button',
30146                         cls : 'btn btn-default',
30147                         html : '<i class="fa fa-undo"></i>'
30148                     }
30149                 ]
30150             },
30151             {
30152                 tag : 'div',
30153                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30154                 action : 'rotate-right',
30155                 cn : [
30156                     {
30157                         tag : 'button',
30158                         cls : 'btn btn-default',
30159                         html : '<i class="fa fa-repeat"></i>'
30160                     }
30161                 ]
30162             }
30163         ]
30164     }
30165 });
30166
30167 /*
30168 * Licence: LGPL
30169 */
30170
30171 /**
30172  * @class Roo.bootstrap.DocumentManager
30173  * @extends Roo.bootstrap.Component
30174  * Bootstrap DocumentManager class
30175  * @cfg {String} paramName default 'imageUpload'
30176  * @cfg {String} toolTipName default 'filename'
30177  * @cfg {String} method default POST
30178  * @cfg {String} url action url
30179  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30180  * @cfg {Boolean} multiple multiple upload default true
30181  * @cfg {Number} thumbSize default 300
30182  * @cfg {String} fieldLabel
30183  * @cfg {Number} labelWidth default 4
30184  * @cfg {String} labelAlign (left|top) default left
30185  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30186 * @cfg {Number} labellg set the width of label (1-12)
30187  * @cfg {Number} labelmd set the width of label (1-12)
30188  * @cfg {Number} labelsm set the width of label (1-12)
30189  * @cfg {Number} labelxs set the width of label (1-12)
30190  * 
30191  * @constructor
30192  * Create a new DocumentManager
30193  * @param {Object} config The config object
30194  */
30195
30196 Roo.bootstrap.DocumentManager = function(config){
30197     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30198     
30199     this.files = [];
30200     this.delegates = [];
30201     
30202     this.addEvents({
30203         /**
30204          * @event initial
30205          * Fire when initial the DocumentManager
30206          * @param {Roo.bootstrap.DocumentManager} this
30207          */
30208         "initial" : true,
30209         /**
30210          * @event inspect
30211          * inspect selected file
30212          * @param {Roo.bootstrap.DocumentManager} this
30213          * @param {File} file
30214          */
30215         "inspect" : true,
30216         /**
30217          * @event exception
30218          * Fire when xhr load exception
30219          * @param {Roo.bootstrap.DocumentManager} this
30220          * @param {XMLHttpRequest} xhr
30221          */
30222         "exception" : true,
30223         /**
30224          * @event afterupload
30225          * Fire when xhr load exception
30226          * @param {Roo.bootstrap.DocumentManager} this
30227          * @param {XMLHttpRequest} xhr
30228          */
30229         "afterupload" : true,
30230         /**
30231          * @event prepare
30232          * prepare the form data
30233          * @param {Roo.bootstrap.DocumentManager} this
30234          * @param {Object} formData
30235          */
30236         "prepare" : true,
30237         /**
30238          * @event remove
30239          * Fire when remove the file
30240          * @param {Roo.bootstrap.DocumentManager} this
30241          * @param {Object} file
30242          */
30243         "remove" : true,
30244         /**
30245          * @event refresh
30246          * Fire after refresh the file
30247          * @param {Roo.bootstrap.DocumentManager} this
30248          */
30249         "refresh" : true,
30250         /**
30251          * @event click
30252          * Fire after click the image
30253          * @param {Roo.bootstrap.DocumentManager} this
30254          * @param {Object} file
30255          */
30256         "click" : true,
30257         /**
30258          * @event edit
30259          * Fire when upload a image and editable set to true
30260          * @param {Roo.bootstrap.DocumentManager} this
30261          * @param {Object} file
30262          */
30263         "edit" : true,
30264         /**
30265          * @event beforeselectfile
30266          * Fire before select file
30267          * @param {Roo.bootstrap.DocumentManager} this
30268          */
30269         "beforeselectfile" : true,
30270         /**
30271          * @event process
30272          * Fire before process file
30273          * @param {Roo.bootstrap.DocumentManager} this
30274          * @param {Object} file
30275          */
30276         "process" : true,
30277         /**
30278          * @event previewrendered
30279          * Fire when preview rendered
30280          * @param {Roo.bootstrap.DocumentManager} this
30281          * @param {Object} file
30282          */
30283         "previewrendered" : true,
30284         /**
30285          */
30286         "previewResize" : true
30287         
30288     });
30289 };
30290
30291 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30292     
30293     boxes : 0,
30294     inputName : '',
30295     thumbSize : 300,
30296     multiple : true,
30297     files : false,
30298     method : 'POST',
30299     url : '',
30300     paramName : 'imageUpload',
30301     toolTipName : 'filename',
30302     fieldLabel : '',
30303     labelWidth : 4,
30304     labelAlign : 'left',
30305     editable : true,
30306     delegates : false,
30307     xhr : false, 
30308     
30309     labellg : 0,
30310     labelmd : 0,
30311     labelsm : 0,
30312     labelxs : 0,
30313     
30314     getAutoCreate : function()
30315     {   
30316         var managerWidget = {
30317             tag : 'div',
30318             cls : 'roo-document-manager',
30319             cn : [
30320                 {
30321                     tag : 'input',
30322                     cls : 'roo-document-manager-selector',
30323                     type : 'file'
30324                 },
30325                 {
30326                     tag : 'div',
30327                     cls : 'roo-document-manager-uploader',
30328                     cn : [
30329                         {
30330                             tag : 'div',
30331                             cls : 'roo-document-manager-upload-btn',
30332                             html : '<i class="fa fa-plus"></i>'
30333                         }
30334                     ]
30335                     
30336                 }
30337             ]
30338         };
30339         
30340         var content = [
30341             {
30342                 tag : 'div',
30343                 cls : 'column col-md-12',
30344                 cn : managerWidget
30345             }
30346         ];
30347         
30348         if(this.fieldLabel.length){
30349             
30350             content = [
30351                 {
30352                     tag : 'div',
30353                     cls : 'column col-md-12',
30354                     html : this.fieldLabel
30355                 },
30356                 {
30357                     tag : 'div',
30358                     cls : 'column col-md-12',
30359                     cn : managerWidget
30360                 }
30361             ];
30362
30363             if(this.labelAlign == 'left'){
30364                 content = [
30365                     {
30366                         tag : 'div',
30367                         cls : 'column',
30368                         html : this.fieldLabel
30369                     },
30370                     {
30371                         tag : 'div',
30372                         cls : 'column',
30373                         cn : managerWidget
30374                     }
30375                 ];
30376                 
30377                 if(this.labelWidth > 12){
30378                     content[0].style = "width: " + this.labelWidth + 'px';
30379                 }
30380
30381                 if(this.labelWidth < 13 && this.labelmd == 0){
30382                     this.labelmd = this.labelWidth;
30383                 }
30384
30385                 if(this.labellg > 0){
30386                     content[0].cls += ' col-lg-' + this.labellg;
30387                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30388                 }
30389
30390                 if(this.labelmd > 0){
30391                     content[0].cls += ' col-md-' + this.labelmd;
30392                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30393                 }
30394
30395                 if(this.labelsm > 0){
30396                     content[0].cls += ' col-sm-' + this.labelsm;
30397                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30398                 }
30399
30400                 if(this.labelxs > 0){
30401                     content[0].cls += ' col-xs-' + this.labelxs;
30402                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30403                 }
30404                 
30405             }
30406         }
30407         
30408         var cfg = {
30409             tag : 'div',
30410             cls : 'row clearfix',
30411             cn : content
30412         };
30413         
30414         return cfg;
30415         
30416     },
30417     
30418     initEvents : function()
30419     {
30420         this.managerEl = this.el.select('.roo-document-manager', true).first();
30421         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30422         
30423         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30424         this.selectorEl.hide();
30425         
30426         if(this.multiple){
30427             this.selectorEl.attr('multiple', 'multiple');
30428         }
30429         
30430         this.selectorEl.on('change', this.onFileSelected, this);
30431         
30432         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30433         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30434         
30435         this.uploader.on('click', this.onUploaderClick, this);
30436         
30437         this.renderProgressDialog();
30438         
30439         var _this = this;
30440         
30441         window.addEventListener("resize", function() { _this.refresh(); } );
30442         
30443         this.fireEvent('initial', this);
30444     },
30445     
30446     renderProgressDialog : function()
30447     {
30448         var _this = this;
30449         
30450         this.progressDialog = new Roo.bootstrap.Modal({
30451             cls : 'roo-document-manager-progress-dialog',
30452             allow_close : false,
30453             animate : false,
30454             title : '',
30455             buttons : [
30456                 {
30457                     name  :'cancel',
30458                     weight : 'danger',
30459                     html : 'Cancel'
30460                 }
30461             ], 
30462             listeners : { 
30463                 btnclick : function() {
30464                     _this.uploadCancel();
30465                     this.hide();
30466                 }
30467             }
30468         });
30469          
30470         this.progressDialog.render(Roo.get(document.body));
30471          
30472         this.progress = new Roo.bootstrap.Progress({
30473             cls : 'roo-document-manager-progress',
30474             active : true,
30475             striped : true
30476         });
30477         
30478         this.progress.render(this.progressDialog.getChildContainer());
30479         
30480         this.progressBar = new Roo.bootstrap.ProgressBar({
30481             cls : 'roo-document-manager-progress-bar',
30482             aria_valuenow : 0,
30483             aria_valuemin : 0,
30484             aria_valuemax : 12,
30485             panel : 'success'
30486         });
30487         
30488         this.progressBar.render(this.progress.getChildContainer());
30489     },
30490     
30491     onUploaderClick : function(e)
30492     {
30493         e.preventDefault();
30494      
30495         if(this.fireEvent('beforeselectfile', this) != false){
30496             this.selectorEl.dom.click();
30497         }
30498         
30499     },
30500     
30501     onFileSelected : function(e)
30502     {
30503         e.preventDefault();
30504         
30505         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30506             return;
30507         }
30508         
30509         Roo.each(this.selectorEl.dom.files, function(file){
30510             if(this.fireEvent('inspect', this, file) != false){
30511                 this.files.push(file);
30512             }
30513         }, this);
30514         
30515         this.queue();
30516         
30517     },
30518     
30519     queue : function()
30520     {
30521         this.selectorEl.dom.value = '';
30522         
30523         if(!this.files || !this.files.length){
30524             return;
30525         }
30526         
30527         if(this.boxes > 0 && this.files.length > this.boxes){
30528             this.files = this.files.slice(0, this.boxes);
30529         }
30530         
30531         this.uploader.show();
30532         
30533         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30534             this.uploader.hide();
30535         }
30536         
30537         var _this = this;
30538         
30539         var files = [];
30540         
30541         var docs = [];
30542         
30543         Roo.each(this.files, function(file){
30544             
30545             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30546                 var f = this.renderPreview(file);
30547                 files.push(f);
30548                 return;
30549             }
30550             
30551             if(file.type.indexOf('image') != -1){
30552                 this.delegates.push(
30553                     (function(){
30554                         _this.process(file);
30555                     }).createDelegate(this)
30556                 );
30557         
30558                 return;
30559             }
30560             
30561             docs.push(
30562                 (function(){
30563                     _this.process(file);
30564                 }).createDelegate(this)
30565             );
30566             
30567         }, this);
30568         
30569         this.files = files;
30570         
30571         this.delegates = this.delegates.concat(docs);
30572         
30573         if(!this.delegates.length){
30574             this.refresh();
30575             return;
30576         }
30577         
30578         this.progressBar.aria_valuemax = this.delegates.length;
30579         
30580         this.arrange();
30581         
30582         return;
30583     },
30584     
30585     arrange : function()
30586     {
30587         if(!this.delegates.length){
30588             this.progressDialog.hide();
30589             this.refresh();
30590             return;
30591         }
30592         
30593         var delegate = this.delegates.shift();
30594         
30595         this.progressDialog.show();
30596         
30597         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30598         
30599         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30600         
30601         delegate();
30602     },
30603     
30604     refresh : function()
30605     {
30606         this.uploader.show();
30607         
30608         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30609             this.uploader.hide();
30610         }
30611         
30612         Roo.isTouch ? this.closable(false) : this.closable(true);
30613         
30614         this.fireEvent('refresh', this);
30615     },
30616     
30617     onRemove : function(e, el, o)
30618     {
30619         e.preventDefault();
30620         
30621         this.fireEvent('remove', this, o);
30622         
30623     },
30624     
30625     remove : function(o)
30626     {
30627         var files = [];
30628         
30629         Roo.each(this.files, function(file){
30630             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30631                 files.push(file);
30632                 return;
30633             }
30634
30635             o.target.remove();
30636
30637         }, this);
30638         
30639         this.files = files;
30640         
30641         this.refresh();
30642     },
30643     
30644     clear : function()
30645     {
30646         Roo.each(this.files, function(file){
30647             if(!file.target){
30648                 return;
30649             }
30650             
30651             file.target.remove();
30652
30653         }, this);
30654         
30655         this.files = [];
30656         
30657         this.refresh();
30658     },
30659     
30660     onClick : function(e, el, o)
30661     {
30662         e.preventDefault();
30663         
30664         this.fireEvent('click', this, o);
30665         
30666     },
30667     
30668     closable : function(closable)
30669     {
30670         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30671             
30672             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30673             
30674             if(closable){
30675                 el.show();
30676                 return;
30677             }
30678             
30679             el.hide();
30680             
30681         }, this);
30682     },
30683     
30684     xhrOnLoad : function(xhr)
30685     {
30686         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30687             el.remove();
30688         }, this);
30689         
30690         if (xhr.readyState !== 4) {
30691             this.arrange();
30692             this.fireEvent('exception', this, xhr);
30693             return;
30694         }
30695
30696         var response = Roo.decode(xhr.responseText);
30697         
30698         if(!response.success){
30699             this.arrange();
30700             this.fireEvent('exception', this, xhr);
30701             return;
30702         }
30703         
30704         var file = this.renderPreview(response.data);
30705         
30706         this.files.push(file);
30707         
30708         this.arrange();
30709         
30710         this.fireEvent('afterupload', this, xhr);
30711         
30712     },
30713     
30714     xhrOnError : function(xhr)
30715     {
30716         Roo.log('xhr on error');
30717         
30718         var response = Roo.decode(xhr.responseText);
30719           
30720         Roo.log(response);
30721         
30722         this.arrange();
30723     },
30724     
30725     process : function(file)
30726     {
30727         if(this.fireEvent('process', this, file) !== false){
30728             if(this.editable && file.type.indexOf('image') != -1){
30729                 this.fireEvent('edit', this, file);
30730                 return;
30731             }
30732
30733             this.uploadStart(file, false);
30734
30735             return;
30736         }
30737         
30738     },
30739     
30740     uploadStart : function(file, crop)
30741     {
30742         this.xhr = new XMLHttpRequest();
30743         
30744         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30745             this.arrange();
30746             return;
30747         }
30748         
30749         file.xhr = this.xhr;
30750             
30751         this.managerEl.createChild({
30752             tag : 'div',
30753             cls : 'roo-document-manager-loading',
30754             cn : [
30755                 {
30756                     tag : 'div',
30757                     tooltip : file.name,
30758                     cls : 'roo-document-manager-thumb',
30759                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30760                 }
30761             ]
30762
30763         });
30764
30765         this.xhr.open(this.method, this.url, true);
30766         
30767         var headers = {
30768             "Accept": "application/json",
30769             "Cache-Control": "no-cache",
30770             "X-Requested-With": "XMLHttpRequest"
30771         };
30772         
30773         for (var headerName in headers) {
30774             var headerValue = headers[headerName];
30775             if (headerValue) {
30776                 this.xhr.setRequestHeader(headerName, headerValue);
30777             }
30778         }
30779         
30780         var _this = this;
30781         
30782         this.xhr.onload = function()
30783         {
30784             _this.xhrOnLoad(_this.xhr);
30785         }
30786         
30787         this.xhr.onerror = function()
30788         {
30789             _this.xhrOnError(_this.xhr);
30790         }
30791         
30792         var formData = new FormData();
30793
30794         formData.append('returnHTML', 'NO');
30795         
30796         if(crop){
30797             formData.append('crop', crop);
30798         }
30799         
30800         formData.append(this.paramName, file, file.name);
30801         
30802         var options = {
30803             file : file, 
30804             manually : false
30805         };
30806         
30807         if(this.fireEvent('prepare', this, formData, options) != false){
30808             
30809             if(options.manually){
30810                 return;
30811             }
30812             
30813             this.xhr.send(formData);
30814             return;
30815         };
30816         
30817         this.uploadCancel();
30818     },
30819     
30820     uploadCancel : function()
30821     {
30822         if (this.xhr) {
30823             this.xhr.abort();
30824         }
30825         
30826         this.delegates = [];
30827         
30828         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30829             el.remove();
30830         }, this);
30831         
30832         this.arrange();
30833     },
30834     
30835     renderPreview : function(file)
30836     {
30837         if(typeof(file.target) != 'undefined' && file.target){
30838             return file;
30839         }
30840         
30841         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30842         
30843         var previewEl = this.managerEl.createChild({
30844             tag : 'div',
30845             cls : 'roo-document-manager-preview',
30846             cn : [
30847                 {
30848                     tag : 'div',
30849                     tooltip : file[this.toolTipName],
30850                     cls : 'roo-document-manager-thumb',
30851                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30852                 },
30853                 {
30854                     tag : 'button',
30855                     cls : 'close',
30856                     html : '<i class="fa fa-times-circle"></i>'
30857                 }
30858             ]
30859         });
30860
30861         var close = previewEl.select('button.close', true).first();
30862
30863         close.on('click', this.onRemove, this, file);
30864
30865         file.target = previewEl;
30866
30867         var image = previewEl.select('img', true).first();
30868         
30869         var _this = this;
30870         
30871         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30872         
30873         image.on('click', this.onClick, this, file);
30874         
30875         this.fireEvent('previewrendered', this, file);
30876         
30877         return file;
30878         
30879     },
30880     
30881     onPreviewLoad : function(file, image)
30882     {
30883         if(typeof(file.target) == 'undefined' || !file.target){
30884             return;
30885         }
30886         
30887         var width = image.dom.naturalWidth || image.dom.width;
30888         var height = image.dom.naturalHeight || image.dom.height;
30889         
30890         if(!this.previewResize) {
30891             return;
30892         }
30893         
30894         if(width > height){
30895             file.target.addClass('wide');
30896             return;
30897         }
30898         
30899         file.target.addClass('tall');
30900         return;
30901         
30902     },
30903     
30904     uploadFromSource : function(file, crop)
30905     {
30906         this.xhr = new XMLHttpRequest();
30907         
30908         this.managerEl.createChild({
30909             tag : 'div',
30910             cls : 'roo-document-manager-loading',
30911             cn : [
30912                 {
30913                     tag : 'div',
30914                     tooltip : file.name,
30915                     cls : 'roo-document-manager-thumb',
30916                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30917                 }
30918             ]
30919
30920         });
30921
30922         this.xhr.open(this.method, this.url, true);
30923         
30924         var headers = {
30925             "Accept": "application/json",
30926             "Cache-Control": "no-cache",
30927             "X-Requested-With": "XMLHttpRequest"
30928         };
30929         
30930         for (var headerName in headers) {
30931             var headerValue = headers[headerName];
30932             if (headerValue) {
30933                 this.xhr.setRequestHeader(headerName, headerValue);
30934             }
30935         }
30936         
30937         var _this = this;
30938         
30939         this.xhr.onload = function()
30940         {
30941             _this.xhrOnLoad(_this.xhr);
30942         }
30943         
30944         this.xhr.onerror = function()
30945         {
30946             _this.xhrOnError(_this.xhr);
30947         }
30948         
30949         var formData = new FormData();
30950
30951         formData.append('returnHTML', 'NO');
30952         
30953         formData.append('crop', crop);
30954         
30955         if(typeof(file.filename) != 'undefined'){
30956             formData.append('filename', file.filename);
30957         }
30958         
30959         if(typeof(file.mimetype) != 'undefined'){
30960             formData.append('mimetype', file.mimetype);
30961         }
30962         
30963         Roo.log(formData);
30964         
30965         if(this.fireEvent('prepare', this, formData) != false){
30966             this.xhr.send(formData);
30967         };
30968     }
30969 });
30970
30971 /*
30972 * Licence: LGPL
30973 */
30974
30975 /**
30976  * @class Roo.bootstrap.DocumentViewer
30977  * @extends Roo.bootstrap.Component
30978  * Bootstrap DocumentViewer class
30979  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30980  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30981  * 
30982  * @constructor
30983  * Create a new DocumentViewer
30984  * @param {Object} config The config object
30985  */
30986
30987 Roo.bootstrap.DocumentViewer = function(config){
30988     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30989     
30990     this.addEvents({
30991         /**
30992          * @event initial
30993          * Fire after initEvent
30994          * @param {Roo.bootstrap.DocumentViewer} this
30995          */
30996         "initial" : true,
30997         /**
30998          * @event click
30999          * Fire after click
31000          * @param {Roo.bootstrap.DocumentViewer} this
31001          */
31002         "click" : true,
31003         /**
31004          * @event download
31005          * Fire after download button
31006          * @param {Roo.bootstrap.DocumentViewer} this
31007          */
31008         "download" : true,
31009         /**
31010          * @event trash
31011          * Fire after trash button
31012          * @param {Roo.bootstrap.DocumentViewer} this
31013          */
31014         "trash" : true
31015         
31016     });
31017 };
31018
31019 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31020     
31021     showDownload : true,
31022     
31023     showTrash : true,
31024     
31025     getAutoCreate : function()
31026     {
31027         var cfg = {
31028             tag : 'div',
31029             cls : 'roo-document-viewer',
31030             cn : [
31031                 {
31032                     tag : 'div',
31033                     cls : 'roo-document-viewer-body',
31034                     cn : [
31035                         {
31036                             tag : 'div',
31037                             cls : 'roo-document-viewer-thumb',
31038                             cn : [
31039                                 {
31040                                     tag : 'img',
31041                                     cls : 'roo-document-viewer-image'
31042                                 }
31043                             ]
31044                         }
31045                     ]
31046                 },
31047                 {
31048                     tag : 'div',
31049                     cls : 'roo-document-viewer-footer',
31050                     cn : {
31051                         tag : 'div',
31052                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31053                         cn : [
31054                             {
31055                                 tag : 'div',
31056                                 cls : 'btn-group roo-document-viewer-download',
31057                                 cn : [
31058                                     {
31059                                         tag : 'button',
31060                                         cls : 'btn btn-default',
31061                                         html : '<i class="fa fa-download"></i>'
31062                                     }
31063                                 ]
31064                             },
31065                             {
31066                                 tag : 'div',
31067                                 cls : 'btn-group roo-document-viewer-trash',
31068                                 cn : [
31069                                     {
31070                                         tag : 'button',
31071                                         cls : 'btn btn-default',
31072                                         html : '<i class="fa fa-trash"></i>'
31073                                     }
31074                                 ]
31075                             }
31076                         ]
31077                     }
31078                 }
31079             ]
31080         };
31081         
31082         return cfg;
31083     },
31084     
31085     initEvents : function()
31086     {
31087         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31088         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31089         
31090         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31091         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31092         
31093         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31094         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31095         
31096         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31097         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31098         
31099         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31100         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31101         
31102         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31103         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31104         
31105         this.bodyEl.on('click', this.onClick, this);
31106         this.downloadBtn.on('click', this.onDownload, this);
31107         this.trashBtn.on('click', this.onTrash, this);
31108         
31109         this.downloadBtn.hide();
31110         this.trashBtn.hide();
31111         
31112         if(this.showDownload){
31113             this.downloadBtn.show();
31114         }
31115         
31116         if(this.showTrash){
31117             this.trashBtn.show();
31118         }
31119         
31120         if(!this.showDownload && !this.showTrash) {
31121             this.footerEl.hide();
31122         }
31123         
31124     },
31125     
31126     initial : function()
31127     {
31128         this.fireEvent('initial', this);
31129         
31130     },
31131     
31132     onClick : function(e)
31133     {
31134         e.preventDefault();
31135         
31136         this.fireEvent('click', this);
31137     },
31138     
31139     onDownload : function(e)
31140     {
31141         e.preventDefault();
31142         
31143         this.fireEvent('download', this);
31144     },
31145     
31146     onTrash : function(e)
31147     {
31148         e.preventDefault();
31149         
31150         this.fireEvent('trash', this);
31151     }
31152     
31153 });
31154 /*
31155  * - LGPL
31156  *
31157  * nav progress bar
31158  * 
31159  */
31160
31161 /**
31162  * @class Roo.bootstrap.NavProgressBar
31163  * @extends Roo.bootstrap.Component
31164  * Bootstrap NavProgressBar class
31165  * 
31166  * @constructor
31167  * Create a new nav progress bar
31168  * @param {Object} config The config object
31169  */
31170
31171 Roo.bootstrap.NavProgressBar = function(config){
31172     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31173
31174     this.bullets = this.bullets || [];
31175    
31176 //    Roo.bootstrap.NavProgressBar.register(this);
31177      this.addEvents({
31178         /**
31179              * @event changed
31180              * Fires when the active item changes
31181              * @param {Roo.bootstrap.NavProgressBar} this
31182              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31183              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31184          */
31185         'changed': true
31186      });
31187     
31188 };
31189
31190 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31191     
31192     bullets : [],
31193     barItems : [],
31194     
31195     getAutoCreate : function()
31196     {
31197         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31198         
31199         cfg = {
31200             tag : 'div',
31201             cls : 'roo-navigation-bar-group',
31202             cn : [
31203                 {
31204                     tag : 'div',
31205                     cls : 'roo-navigation-top-bar'
31206                 },
31207                 {
31208                     tag : 'div',
31209                     cls : 'roo-navigation-bullets-bar',
31210                     cn : [
31211                         {
31212                             tag : 'ul',
31213                             cls : 'roo-navigation-bar'
31214                         }
31215                     ]
31216                 },
31217                 
31218                 {
31219                     tag : 'div',
31220                     cls : 'roo-navigation-bottom-bar'
31221                 }
31222             ]
31223             
31224         };
31225         
31226         return cfg;
31227         
31228     },
31229     
31230     initEvents: function() 
31231     {
31232         
31233     },
31234     
31235     onRender : function(ct, position) 
31236     {
31237         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31238         
31239         if(this.bullets.length){
31240             Roo.each(this.bullets, function(b){
31241                this.addItem(b);
31242             }, this);
31243         }
31244         
31245         this.format();
31246         
31247     },
31248     
31249     addItem : function(cfg)
31250     {
31251         var item = new Roo.bootstrap.NavProgressItem(cfg);
31252         
31253         item.parentId = this.id;
31254         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31255         
31256         if(cfg.html){
31257             var top = new Roo.bootstrap.Element({
31258                 tag : 'div',
31259                 cls : 'roo-navigation-bar-text'
31260             });
31261             
31262             var bottom = new Roo.bootstrap.Element({
31263                 tag : 'div',
31264                 cls : 'roo-navigation-bar-text'
31265             });
31266             
31267             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31268             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31269             
31270             var topText = new Roo.bootstrap.Element({
31271                 tag : 'span',
31272                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31273             });
31274             
31275             var bottomText = new Roo.bootstrap.Element({
31276                 tag : 'span',
31277                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31278             });
31279             
31280             topText.onRender(top.el, null);
31281             bottomText.onRender(bottom.el, null);
31282             
31283             item.topEl = top;
31284             item.bottomEl = bottom;
31285         }
31286         
31287         this.barItems.push(item);
31288         
31289         return item;
31290     },
31291     
31292     getActive : function()
31293     {
31294         var active = false;
31295         
31296         Roo.each(this.barItems, function(v){
31297             
31298             if (!v.isActive()) {
31299                 return;
31300             }
31301             
31302             active = v;
31303             return false;
31304             
31305         });
31306         
31307         return active;
31308     },
31309     
31310     setActiveItem : function(item)
31311     {
31312         var prev = false;
31313         
31314         Roo.each(this.barItems, function(v){
31315             if (v.rid == item.rid) {
31316                 return ;
31317             }
31318             
31319             if (v.isActive()) {
31320                 v.setActive(false);
31321                 prev = v;
31322             }
31323         });
31324
31325         item.setActive(true);
31326         
31327         this.fireEvent('changed', this, item, prev);
31328     },
31329     
31330     getBarItem: function(rid)
31331     {
31332         var ret = false;
31333         
31334         Roo.each(this.barItems, function(e) {
31335             if (e.rid != rid) {
31336                 return;
31337             }
31338             
31339             ret =  e;
31340             return false;
31341         });
31342         
31343         return ret;
31344     },
31345     
31346     indexOfItem : function(item)
31347     {
31348         var index = false;
31349         
31350         Roo.each(this.barItems, function(v, i){
31351             
31352             if (v.rid != item.rid) {
31353                 return;
31354             }
31355             
31356             index = i;
31357             return false
31358         });
31359         
31360         return index;
31361     },
31362     
31363     setActiveNext : function()
31364     {
31365         var i = this.indexOfItem(this.getActive());
31366         
31367         if (i > this.barItems.length) {
31368             return;
31369         }
31370         
31371         this.setActiveItem(this.barItems[i+1]);
31372     },
31373     
31374     setActivePrev : function()
31375     {
31376         var i = this.indexOfItem(this.getActive());
31377         
31378         if (i  < 1) {
31379             return;
31380         }
31381         
31382         this.setActiveItem(this.barItems[i-1]);
31383     },
31384     
31385     format : function()
31386     {
31387         if(!this.barItems.length){
31388             return;
31389         }
31390      
31391         var width = 100 / this.barItems.length;
31392         
31393         Roo.each(this.barItems, function(i){
31394             i.el.setStyle('width', width + '%');
31395             i.topEl.el.setStyle('width', width + '%');
31396             i.bottomEl.el.setStyle('width', width + '%');
31397         }, this);
31398         
31399     }
31400     
31401 });
31402 /*
31403  * - LGPL
31404  *
31405  * Nav Progress Item
31406  * 
31407  */
31408
31409 /**
31410  * @class Roo.bootstrap.NavProgressItem
31411  * @extends Roo.bootstrap.Component
31412  * Bootstrap NavProgressItem class
31413  * @cfg {String} rid the reference id
31414  * @cfg {Boolean} active (true|false) Is item active default false
31415  * @cfg {Boolean} disabled (true|false) Is item active default false
31416  * @cfg {String} html
31417  * @cfg {String} position (top|bottom) text position default bottom
31418  * @cfg {String} icon show icon instead of number
31419  * 
31420  * @constructor
31421  * Create a new NavProgressItem
31422  * @param {Object} config The config object
31423  */
31424 Roo.bootstrap.NavProgressItem = function(config){
31425     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31426     this.addEvents({
31427         // raw events
31428         /**
31429          * @event click
31430          * The raw click event for the entire grid.
31431          * @param {Roo.bootstrap.NavProgressItem} this
31432          * @param {Roo.EventObject} e
31433          */
31434         "click" : true
31435     });
31436    
31437 };
31438
31439 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31440     
31441     rid : '',
31442     active : false,
31443     disabled : false,
31444     html : '',
31445     position : 'bottom',
31446     icon : false,
31447     
31448     getAutoCreate : function()
31449     {
31450         var iconCls = 'roo-navigation-bar-item-icon';
31451         
31452         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31453         
31454         var cfg = {
31455             tag: 'li',
31456             cls: 'roo-navigation-bar-item',
31457             cn : [
31458                 {
31459                     tag : 'i',
31460                     cls : iconCls
31461                 }
31462             ]
31463         };
31464         
31465         if(this.active){
31466             cfg.cls += ' active';
31467         }
31468         if(this.disabled){
31469             cfg.cls += ' disabled';
31470         }
31471         
31472         return cfg;
31473     },
31474     
31475     disable : function()
31476     {
31477         this.setDisabled(true);
31478     },
31479     
31480     enable : function()
31481     {
31482         this.setDisabled(false);
31483     },
31484     
31485     initEvents: function() 
31486     {
31487         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31488         
31489         this.iconEl.on('click', this.onClick, this);
31490     },
31491     
31492     onClick : function(e)
31493     {
31494         e.preventDefault();
31495         
31496         if(this.disabled){
31497             return;
31498         }
31499         
31500         if(this.fireEvent('click', this, e) === false){
31501             return;
31502         };
31503         
31504         this.parent().setActiveItem(this);
31505     },
31506     
31507     isActive: function () 
31508     {
31509         return this.active;
31510     },
31511     
31512     setActive : function(state)
31513     {
31514         if(this.active == state){
31515             return;
31516         }
31517         
31518         this.active = state;
31519         
31520         if (state) {
31521             this.el.addClass('active');
31522             return;
31523         }
31524         
31525         this.el.removeClass('active');
31526         
31527         return;
31528     },
31529     
31530     setDisabled : function(state)
31531     {
31532         if(this.disabled == state){
31533             return;
31534         }
31535         
31536         this.disabled = state;
31537         
31538         if (state) {
31539             this.el.addClass('disabled');
31540             return;
31541         }
31542         
31543         this.el.removeClass('disabled');
31544     },
31545     
31546     tooltipEl : function()
31547     {
31548         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31549     }
31550 });
31551  
31552
31553  /*
31554  * - LGPL
31555  *
31556  * FieldLabel
31557  * 
31558  */
31559
31560 /**
31561  * @class Roo.bootstrap.FieldLabel
31562  * @extends Roo.bootstrap.Component
31563  * Bootstrap FieldLabel class
31564  * @cfg {String} html contents of the element
31565  * @cfg {String} tag tag of the element default label
31566  * @cfg {String} cls class of the element
31567  * @cfg {String} target label target 
31568  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31569  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31570  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31571  * @cfg {String} iconTooltip default "This field is required"
31572  * @cfg {String} indicatorpos (left|right) default left
31573  * 
31574  * @constructor
31575  * Create a new FieldLabel
31576  * @param {Object} config The config object
31577  */
31578
31579 Roo.bootstrap.FieldLabel = function(config){
31580     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31581     
31582     this.addEvents({
31583             /**
31584              * @event invalid
31585              * Fires after the field has been marked as invalid.
31586              * @param {Roo.form.FieldLabel} this
31587              * @param {String} msg The validation message
31588              */
31589             invalid : true,
31590             /**
31591              * @event valid
31592              * Fires after the field has been validated with no errors.
31593              * @param {Roo.form.FieldLabel} this
31594              */
31595             valid : true
31596         });
31597 };
31598
31599 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31600     
31601     tag: 'label',
31602     cls: '',
31603     html: '',
31604     target: '',
31605     allowBlank : true,
31606     invalidClass : 'has-warning',
31607     validClass : 'has-success',
31608     iconTooltip : 'This field is required',
31609     indicatorpos : 'left',
31610     
31611     getAutoCreate : function(){
31612         
31613         var cls = "";
31614         if (!this.allowBlank) {
31615             cls  = "visible";
31616         }
31617         
31618         var cfg = {
31619             tag : this.tag,
31620             cls : 'roo-bootstrap-field-label ' + this.cls,
31621             for : this.target,
31622             cn : [
31623                 {
31624                     tag : 'i',
31625                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31626                     tooltip : this.iconTooltip
31627                 },
31628                 {
31629                     tag : 'span',
31630                     html : this.html
31631                 }
31632             ] 
31633         };
31634         
31635         if(this.indicatorpos == 'right'){
31636             var cfg = {
31637                 tag : this.tag,
31638                 cls : 'roo-bootstrap-field-label ' + this.cls,
31639                 for : this.target,
31640                 cn : [
31641                     {
31642                         tag : 'span',
31643                         html : this.html
31644                     },
31645                     {
31646                         tag : 'i',
31647                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31648                         tooltip : this.iconTooltip
31649                     }
31650                 ] 
31651             };
31652         }
31653         
31654         return cfg;
31655     },
31656     
31657     initEvents: function() 
31658     {
31659         Roo.bootstrap.Element.superclass.initEvents.call(this);
31660         
31661         this.indicator = this.indicatorEl();
31662         
31663         if(this.indicator){
31664             this.indicator.removeClass('visible');
31665             this.indicator.addClass('invisible');
31666         }
31667         
31668         Roo.bootstrap.FieldLabel.register(this);
31669     },
31670     
31671     indicatorEl : function()
31672     {
31673         var indicator = this.el.select('i.roo-required-indicator',true).first();
31674         
31675         if(!indicator){
31676             return false;
31677         }
31678         
31679         return indicator;
31680         
31681     },
31682     
31683     /**
31684      * Mark this field as valid
31685      */
31686     markValid : function()
31687     {
31688         if(this.indicator){
31689             this.indicator.removeClass('visible');
31690             this.indicator.addClass('invisible');
31691         }
31692         if (Roo.bootstrap.version == 3) {
31693             this.el.removeClass(this.invalidClass);
31694             this.el.addClass(this.validClass);
31695         } else {
31696             this.el.removeClass('is-invalid');
31697             this.el.addClass('is-valid');
31698         }
31699         
31700         
31701         this.fireEvent('valid', this);
31702     },
31703     
31704     /**
31705      * Mark this field as invalid
31706      * @param {String} msg The validation message
31707      */
31708     markInvalid : function(msg)
31709     {
31710         if(this.indicator){
31711             this.indicator.removeClass('invisible');
31712             this.indicator.addClass('visible');
31713         }
31714           if (Roo.bootstrap.version == 3) {
31715             this.el.removeClass(this.validClass);
31716             this.el.addClass(this.invalidClass);
31717         } else {
31718             this.el.removeClass('is-valid');
31719             this.el.addClass('is-invalid');
31720         }
31721         
31722         
31723         this.fireEvent('invalid', this, msg);
31724     }
31725     
31726    
31727 });
31728
31729 Roo.apply(Roo.bootstrap.FieldLabel, {
31730     
31731     groups: {},
31732     
31733      /**
31734     * register a FieldLabel Group
31735     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31736     */
31737     register : function(label)
31738     {
31739         if(this.groups.hasOwnProperty(label.target)){
31740             return;
31741         }
31742      
31743         this.groups[label.target] = label;
31744         
31745     },
31746     /**
31747     * fetch a FieldLabel Group based on the target
31748     * @param {string} target
31749     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31750     */
31751     get: function(target) {
31752         if (typeof(this.groups[target]) == 'undefined') {
31753             return false;
31754         }
31755         
31756         return this.groups[target] ;
31757     }
31758 });
31759
31760  
31761
31762  /*
31763  * - LGPL
31764  *
31765  * page DateSplitField.
31766  * 
31767  */
31768
31769
31770 /**
31771  * @class Roo.bootstrap.DateSplitField
31772  * @extends Roo.bootstrap.Component
31773  * Bootstrap DateSplitField class
31774  * @cfg {string} fieldLabel - the label associated
31775  * @cfg {Number} labelWidth set the width of label (0-12)
31776  * @cfg {String} labelAlign (top|left)
31777  * @cfg {Boolean} dayAllowBlank (true|false) default false
31778  * @cfg {Boolean} monthAllowBlank (true|false) default false
31779  * @cfg {Boolean} yearAllowBlank (true|false) default false
31780  * @cfg {string} dayPlaceholder 
31781  * @cfg {string} monthPlaceholder
31782  * @cfg {string} yearPlaceholder
31783  * @cfg {string} dayFormat default 'd'
31784  * @cfg {string} monthFormat default 'm'
31785  * @cfg {string} yearFormat default 'Y'
31786  * @cfg {Number} labellg set the width of label (1-12)
31787  * @cfg {Number} labelmd set the width of label (1-12)
31788  * @cfg {Number} labelsm set the width of label (1-12)
31789  * @cfg {Number} labelxs set the width of label (1-12)
31790
31791  *     
31792  * @constructor
31793  * Create a new DateSplitField
31794  * @param {Object} config The config object
31795  */
31796
31797 Roo.bootstrap.DateSplitField = function(config){
31798     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31799     
31800     this.addEvents({
31801         // raw events
31802          /**
31803          * @event years
31804          * getting the data of years
31805          * @param {Roo.bootstrap.DateSplitField} this
31806          * @param {Object} years
31807          */
31808         "years" : true,
31809         /**
31810          * @event days
31811          * getting the data of days
31812          * @param {Roo.bootstrap.DateSplitField} this
31813          * @param {Object} days
31814          */
31815         "days" : true,
31816         /**
31817          * @event invalid
31818          * Fires after the field has been marked as invalid.
31819          * @param {Roo.form.Field} this
31820          * @param {String} msg The validation message
31821          */
31822         invalid : true,
31823        /**
31824          * @event valid
31825          * Fires after the field has been validated with no errors.
31826          * @param {Roo.form.Field} this
31827          */
31828         valid : true
31829     });
31830 };
31831
31832 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31833     
31834     fieldLabel : '',
31835     labelAlign : 'top',
31836     labelWidth : 3,
31837     dayAllowBlank : false,
31838     monthAllowBlank : false,
31839     yearAllowBlank : false,
31840     dayPlaceholder : '',
31841     monthPlaceholder : '',
31842     yearPlaceholder : '',
31843     dayFormat : 'd',
31844     monthFormat : 'm',
31845     yearFormat : 'Y',
31846     isFormField : true,
31847     labellg : 0,
31848     labelmd : 0,
31849     labelsm : 0,
31850     labelxs : 0,
31851     
31852     getAutoCreate : function()
31853     {
31854         var cfg = {
31855             tag : 'div',
31856             cls : 'row roo-date-split-field-group',
31857             cn : [
31858                 {
31859                     tag : 'input',
31860                     type : 'hidden',
31861                     cls : 'form-hidden-field roo-date-split-field-group-value',
31862                     name : this.name
31863                 }
31864             ]
31865         };
31866         
31867         var labelCls = 'col-md-12';
31868         var contentCls = 'col-md-4';
31869         
31870         if(this.fieldLabel){
31871             
31872             var label = {
31873                 tag : 'div',
31874                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31875                 cn : [
31876                     {
31877                         tag : 'label',
31878                         html : this.fieldLabel
31879                     }
31880                 ]
31881             };
31882             
31883             if(this.labelAlign == 'left'){
31884             
31885                 if(this.labelWidth > 12){
31886                     label.style = "width: " + this.labelWidth + 'px';
31887                 }
31888
31889                 if(this.labelWidth < 13 && this.labelmd == 0){
31890                     this.labelmd = this.labelWidth;
31891                 }
31892
31893                 if(this.labellg > 0){
31894                     labelCls = ' col-lg-' + this.labellg;
31895                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31896                 }
31897
31898                 if(this.labelmd > 0){
31899                     labelCls = ' col-md-' + this.labelmd;
31900                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31901                 }
31902
31903                 if(this.labelsm > 0){
31904                     labelCls = ' col-sm-' + this.labelsm;
31905                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31906                 }
31907
31908                 if(this.labelxs > 0){
31909                     labelCls = ' col-xs-' + this.labelxs;
31910                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31911                 }
31912             }
31913             
31914             label.cls += ' ' + labelCls;
31915             
31916             cfg.cn.push(label);
31917         }
31918         
31919         Roo.each(['day', 'month', 'year'], function(t){
31920             cfg.cn.push({
31921                 tag : 'div',
31922                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31923             });
31924         }, this);
31925         
31926         return cfg;
31927     },
31928     
31929     inputEl: function ()
31930     {
31931         return this.el.select('.roo-date-split-field-group-value', true).first();
31932     },
31933     
31934     onRender : function(ct, position) 
31935     {
31936         var _this = this;
31937         
31938         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31939         
31940         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31941         
31942         this.dayField = new Roo.bootstrap.ComboBox({
31943             allowBlank : this.dayAllowBlank,
31944             alwaysQuery : true,
31945             displayField : 'value',
31946             editable : false,
31947             fieldLabel : '',
31948             forceSelection : true,
31949             mode : 'local',
31950             placeholder : this.dayPlaceholder,
31951             selectOnFocus : true,
31952             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31953             triggerAction : 'all',
31954             typeAhead : true,
31955             valueField : 'value',
31956             store : new Roo.data.SimpleStore({
31957                 data : (function() {    
31958                     var days = [];
31959                     _this.fireEvent('days', _this, days);
31960                     return days;
31961                 })(),
31962                 fields : [ 'value' ]
31963             }),
31964             listeners : {
31965                 select : function (_self, record, index)
31966                 {
31967                     _this.setValue(_this.getValue());
31968                 }
31969             }
31970         });
31971
31972         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31973         
31974         this.monthField = new Roo.bootstrap.MonthField({
31975             after : '<i class=\"fa fa-calendar\"></i>',
31976             allowBlank : this.monthAllowBlank,
31977             placeholder : this.monthPlaceholder,
31978             readOnly : true,
31979             listeners : {
31980                 render : function (_self)
31981                 {
31982                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31983                         e.preventDefault();
31984                         _self.focus();
31985                     });
31986                 },
31987                 select : function (_self, oldvalue, newvalue)
31988                 {
31989                     _this.setValue(_this.getValue());
31990                 }
31991             }
31992         });
31993         
31994         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31995         
31996         this.yearField = new Roo.bootstrap.ComboBox({
31997             allowBlank : this.yearAllowBlank,
31998             alwaysQuery : true,
31999             displayField : 'value',
32000             editable : false,
32001             fieldLabel : '',
32002             forceSelection : true,
32003             mode : 'local',
32004             placeholder : this.yearPlaceholder,
32005             selectOnFocus : true,
32006             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32007             triggerAction : 'all',
32008             typeAhead : true,
32009             valueField : 'value',
32010             store : new Roo.data.SimpleStore({
32011                 data : (function() {
32012                     var years = [];
32013                     _this.fireEvent('years', _this, years);
32014                     return years;
32015                 })(),
32016                 fields : [ 'value' ]
32017             }),
32018             listeners : {
32019                 select : function (_self, record, index)
32020                 {
32021                     _this.setValue(_this.getValue());
32022                 }
32023             }
32024         });
32025
32026         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32027     },
32028     
32029     setValue : function(v, format)
32030     {
32031         this.inputEl.dom.value = v;
32032         
32033         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32034         
32035         var d = Date.parseDate(v, f);
32036         
32037         if(!d){
32038             this.validate();
32039             return;
32040         }
32041         
32042         this.setDay(d.format(this.dayFormat));
32043         this.setMonth(d.format(this.monthFormat));
32044         this.setYear(d.format(this.yearFormat));
32045         
32046         this.validate();
32047         
32048         return;
32049     },
32050     
32051     setDay : function(v)
32052     {
32053         this.dayField.setValue(v);
32054         this.inputEl.dom.value = this.getValue();
32055         this.validate();
32056         return;
32057     },
32058     
32059     setMonth : function(v)
32060     {
32061         this.monthField.setValue(v, true);
32062         this.inputEl.dom.value = this.getValue();
32063         this.validate();
32064         return;
32065     },
32066     
32067     setYear : function(v)
32068     {
32069         this.yearField.setValue(v);
32070         this.inputEl.dom.value = this.getValue();
32071         this.validate();
32072         return;
32073     },
32074     
32075     getDay : function()
32076     {
32077         return this.dayField.getValue();
32078     },
32079     
32080     getMonth : function()
32081     {
32082         return this.monthField.getValue();
32083     },
32084     
32085     getYear : function()
32086     {
32087         return this.yearField.getValue();
32088     },
32089     
32090     getValue : function()
32091     {
32092         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32093         
32094         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32095         
32096         return date;
32097     },
32098     
32099     reset : function()
32100     {
32101         this.setDay('');
32102         this.setMonth('');
32103         this.setYear('');
32104         this.inputEl.dom.value = '';
32105         this.validate();
32106         return;
32107     },
32108     
32109     validate : function()
32110     {
32111         var d = this.dayField.validate();
32112         var m = this.monthField.validate();
32113         var y = this.yearField.validate();
32114         
32115         var valid = true;
32116         
32117         if(
32118                 (!this.dayAllowBlank && !d) ||
32119                 (!this.monthAllowBlank && !m) ||
32120                 (!this.yearAllowBlank && !y)
32121         ){
32122             valid = false;
32123         }
32124         
32125         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32126             return valid;
32127         }
32128         
32129         if(valid){
32130             this.markValid();
32131             return valid;
32132         }
32133         
32134         this.markInvalid();
32135         
32136         return valid;
32137     },
32138     
32139     markValid : function()
32140     {
32141         
32142         var label = this.el.select('label', true).first();
32143         var icon = this.el.select('i.fa-star', true).first();
32144
32145         if(label && icon){
32146             icon.remove();
32147         }
32148         
32149         this.fireEvent('valid', this);
32150     },
32151     
32152      /**
32153      * Mark this field as invalid
32154      * @param {String} msg The validation message
32155      */
32156     markInvalid : function(msg)
32157     {
32158         
32159         var label = this.el.select('label', true).first();
32160         var icon = this.el.select('i.fa-star', true).first();
32161
32162         if(label && !icon){
32163             this.el.select('.roo-date-split-field-label', true).createChild({
32164                 tag : 'i',
32165                 cls : 'text-danger fa fa-lg fa-star',
32166                 tooltip : 'This field is required',
32167                 style : 'margin-right:5px;'
32168             }, label, true);
32169         }
32170         
32171         this.fireEvent('invalid', this, msg);
32172     },
32173     
32174     clearInvalid : function()
32175     {
32176         var label = this.el.select('label', true).first();
32177         var icon = this.el.select('i.fa-star', true).first();
32178
32179         if(label && icon){
32180             icon.remove();
32181         }
32182         
32183         this.fireEvent('valid', this);
32184     },
32185     
32186     getName: function()
32187     {
32188         return this.name;
32189     }
32190     
32191 });
32192
32193  /**
32194  *
32195  * This is based on 
32196  * http://masonry.desandro.com
32197  *
32198  * The idea is to render all the bricks based on vertical width...
32199  *
32200  * The original code extends 'outlayer' - we might need to use that....
32201  * 
32202  */
32203
32204
32205 /**
32206  * @class Roo.bootstrap.LayoutMasonry
32207  * @extends Roo.bootstrap.Component
32208  * Bootstrap Layout Masonry class
32209  * 
32210  * @constructor
32211  * Create a new Element
32212  * @param {Object} config The config object
32213  */
32214
32215 Roo.bootstrap.LayoutMasonry = function(config){
32216     
32217     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32218     
32219     this.bricks = [];
32220     
32221     Roo.bootstrap.LayoutMasonry.register(this);
32222     
32223     this.addEvents({
32224         // raw events
32225         /**
32226          * @event layout
32227          * Fire after layout the items
32228          * @param {Roo.bootstrap.LayoutMasonry} this
32229          * @param {Roo.EventObject} e
32230          */
32231         "layout" : true
32232     });
32233     
32234 };
32235
32236 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32237     
32238     /**
32239      * @cfg {Boolean} isLayoutInstant = no animation?
32240      */   
32241     isLayoutInstant : false, // needed?
32242    
32243     /**
32244      * @cfg {Number} boxWidth  width of the columns
32245      */   
32246     boxWidth : 450,
32247     
32248       /**
32249      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32250      */   
32251     boxHeight : 0,
32252     
32253     /**
32254      * @cfg {Number} padWidth padding below box..
32255      */   
32256     padWidth : 10, 
32257     
32258     /**
32259      * @cfg {Number} gutter gutter width..
32260      */   
32261     gutter : 10,
32262     
32263      /**
32264      * @cfg {Number} maxCols maximum number of columns
32265      */   
32266     
32267     maxCols: 0,
32268     
32269     /**
32270      * @cfg {Boolean} isAutoInitial defalut true
32271      */   
32272     isAutoInitial : true, 
32273     
32274     containerWidth: 0,
32275     
32276     /**
32277      * @cfg {Boolean} isHorizontal defalut false
32278      */   
32279     isHorizontal : false, 
32280
32281     currentSize : null,
32282     
32283     tag: 'div',
32284     
32285     cls: '',
32286     
32287     bricks: null, //CompositeElement
32288     
32289     cols : 1,
32290     
32291     _isLayoutInited : false,
32292     
32293 //    isAlternative : false, // only use for vertical layout...
32294     
32295     /**
32296      * @cfg {Number} alternativePadWidth padding below box..
32297      */   
32298     alternativePadWidth : 50,
32299     
32300     selectedBrick : [],
32301     
32302     getAutoCreate : function(){
32303         
32304         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32305         
32306         var cfg = {
32307             tag: this.tag,
32308             cls: 'blog-masonary-wrapper ' + this.cls,
32309             cn : {
32310                 cls : 'mas-boxes masonary'
32311             }
32312         };
32313         
32314         return cfg;
32315     },
32316     
32317     getChildContainer: function( )
32318     {
32319         if (this.boxesEl) {
32320             return this.boxesEl;
32321         }
32322         
32323         this.boxesEl = this.el.select('.mas-boxes').first();
32324         
32325         return this.boxesEl;
32326     },
32327     
32328     
32329     initEvents : function()
32330     {
32331         var _this = this;
32332         
32333         if(this.isAutoInitial){
32334             Roo.log('hook children rendered');
32335             this.on('childrenrendered', function() {
32336                 Roo.log('children rendered');
32337                 _this.initial();
32338             } ,this);
32339         }
32340     },
32341     
32342     initial : function()
32343     {
32344         this.selectedBrick = [];
32345         
32346         this.currentSize = this.el.getBox(true);
32347         
32348         Roo.EventManager.onWindowResize(this.resize, this); 
32349
32350         if(!this.isAutoInitial){
32351             this.layout();
32352             return;
32353         }
32354         
32355         this.layout();
32356         
32357         return;
32358         //this.layout.defer(500,this);
32359         
32360     },
32361     
32362     resize : function()
32363     {
32364         var cs = this.el.getBox(true);
32365         
32366         if (
32367                 this.currentSize.width == cs.width && 
32368                 this.currentSize.x == cs.x && 
32369                 this.currentSize.height == cs.height && 
32370                 this.currentSize.y == cs.y 
32371         ) {
32372             Roo.log("no change in with or X or Y");
32373             return;
32374         }
32375         
32376         this.currentSize = cs;
32377         
32378         this.layout();
32379         
32380     },
32381     
32382     layout : function()
32383     {   
32384         this._resetLayout();
32385         
32386         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32387         
32388         this.layoutItems( isInstant );
32389       
32390         this._isLayoutInited = true;
32391         
32392         this.fireEvent('layout', this);
32393         
32394     },
32395     
32396     _resetLayout : function()
32397     {
32398         if(this.isHorizontal){
32399             this.horizontalMeasureColumns();
32400             return;
32401         }
32402         
32403         this.verticalMeasureColumns();
32404         
32405     },
32406     
32407     verticalMeasureColumns : function()
32408     {
32409         this.getContainerWidth();
32410         
32411 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32412 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32413 //            return;
32414 //        }
32415         
32416         var boxWidth = this.boxWidth + this.padWidth;
32417         
32418         if(this.containerWidth < this.boxWidth){
32419             boxWidth = this.containerWidth
32420         }
32421         
32422         var containerWidth = this.containerWidth;
32423         
32424         var cols = Math.floor(containerWidth / boxWidth);
32425         
32426         this.cols = Math.max( cols, 1 );
32427         
32428         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32429         
32430         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32431         
32432         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32433         
32434         this.colWidth = boxWidth + avail - this.padWidth;
32435         
32436         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32437         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32438     },
32439     
32440     horizontalMeasureColumns : function()
32441     {
32442         this.getContainerWidth();
32443         
32444         var boxWidth = this.boxWidth;
32445         
32446         if(this.containerWidth < boxWidth){
32447             boxWidth = this.containerWidth;
32448         }
32449         
32450         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32451         
32452         this.el.setHeight(boxWidth);
32453         
32454     },
32455     
32456     getContainerWidth : function()
32457     {
32458         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32459     },
32460     
32461     layoutItems : function( isInstant )
32462     {
32463         Roo.log(this.bricks);
32464         
32465         var items = Roo.apply([], this.bricks);
32466         
32467         if(this.isHorizontal){
32468             this._horizontalLayoutItems( items , isInstant );
32469             return;
32470         }
32471         
32472 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32473 //            this._verticalAlternativeLayoutItems( items , isInstant );
32474 //            return;
32475 //        }
32476         
32477         this._verticalLayoutItems( items , isInstant );
32478         
32479     },
32480     
32481     _verticalLayoutItems : function ( items , isInstant)
32482     {
32483         if ( !items || !items.length ) {
32484             return;
32485         }
32486         
32487         var standard = [
32488             ['xs', 'xs', 'xs', 'tall'],
32489             ['xs', 'xs', 'tall'],
32490             ['xs', 'xs', 'sm'],
32491             ['xs', 'xs', 'xs'],
32492             ['xs', 'tall'],
32493             ['xs', 'sm'],
32494             ['xs', 'xs'],
32495             ['xs'],
32496             
32497             ['sm', 'xs', 'xs'],
32498             ['sm', 'xs'],
32499             ['sm'],
32500             
32501             ['tall', 'xs', 'xs', 'xs'],
32502             ['tall', 'xs', 'xs'],
32503             ['tall', 'xs'],
32504             ['tall']
32505             
32506         ];
32507         
32508         var queue = [];
32509         
32510         var boxes = [];
32511         
32512         var box = [];
32513         
32514         Roo.each(items, function(item, k){
32515             
32516             switch (item.size) {
32517                 // these layouts take up a full box,
32518                 case 'md' :
32519                 case 'md-left' :
32520                 case 'md-right' :
32521                 case 'wide' :
32522                     
32523                     if(box.length){
32524                         boxes.push(box);
32525                         box = [];
32526                     }
32527                     
32528                     boxes.push([item]);
32529                     
32530                     break;
32531                     
32532                 case 'xs' :
32533                 case 'sm' :
32534                 case 'tall' :
32535                     
32536                     box.push(item);
32537                     
32538                     break;
32539                 default :
32540                     break;
32541                     
32542             }
32543             
32544         }, this);
32545         
32546         if(box.length){
32547             boxes.push(box);
32548             box = [];
32549         }
32550         
32551         var filterPattern = function(box, length)
32552         {
32553             if(!box.length){
32554                 return;
32555             }
32556             
32557             var match = false;
32558             
32559             var pattern = box.slice(0, length);
32560             
32561             var format = [];
32562             
32563             Roo.each(pattern, function(i){
32564                 format.push(i.size);
32565             }, this);
32566             
32567             Roo.each(standard, function(s){
32568                 
32569                 if(String(s) != String(format)){
32570                     return;
32571                 }
32572                 
32573                 match = true;
32574                 return false;
32575                 
32576             }, this);
32577             
32578             if(!match && length == 1){
32579                 return;
32580             }
32581             
32582             if(!match){
32583                 filterPattern(box, length - 1);
32584                 return;
32585             }
32586                 
32587             queue.push(pattern);
32588
32589             box = box.slice(length, box.length);
32590
32591             filterPattern(box, 4);
32592
32593             return;
32594             
32595         }
32596         
32597         Roo.each(boxes, function(box, k){
32598             
32599             if(!box.length){
32600                 return;
32601             }
32602             
32603             if(box.length == 1){
32604                 queue.push(box);
32605                 return;
32606             }
32607             
32608             filterPattern(box, 4);
32609             
32610         }, this);
32611         
32612         this._processVerticalLayoutQueue( queue, isInstant );
32613         
32614     },
32615     
32616 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32617 //    {
32618 //        if ( !items || !items.length ) {
32619 //            return;
32620 //        }
32621 //
32622 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32623 //        
32624 //    },
32625     
32626     _horizontalLayoutItems : function ( items , isInstant)
32627     {
32628         if ( !items || !items.length || items.length < 3) {
32629             return;
32630         }
32631         
32632         items.reverse();
32633         
32634         var eItems = items.slice(0, 3);
32635         
32636         items = items.slice(3, items.length);
32637         
32638         var standard = [
32639             ['xs', 'xs', 'xs', 'wide'],
32640             ['xs', 'xs', 'wide'],
32641             ['xs', 'xs', 'sm'],
32642             ['xs', 'xs', 'xs'],
32643             ['xs', 'wide'],
32644             ['xs', 'sm'],
32645             ['xs', 'xs'],
32646             ['xs'],
32647             
32648             ['sm', 'xs', 'xs'],
32649             ['sm', 'xs'],
32650             ['sm'],
32651             
32652             ['wide', 'xs', 'xs', 'xs'],
32653             ['wide', 'xs', 'xs'],
32654             ['wide', 'xs'],
32655             ['wide'],
32656             
32657             ['wide-thin']
32658         ];
32659         
32660         var queue = [];
32661         
32662         var boxes = [];
32663         
32664         var box = [];
32665         
32666         Roo.each(items, function(item, k){
32667             
32668             switch (item.size) {
32669                 case 'md' :
32670                 case 'md-left' :
32671                 case 'md-right' :
32672                 case 'tall' :
32673                     
32674                     if(box.length){
32675                         boxes.push(box);
32676                         box = [];
32677                     }
32678                     
32679                     boxes.push([item]);
32680                     
32681                     break;
32682                     
32683                 case 'xs' :
32684                 case 'sm' :
32685                 case 'wide' :
32686                 case 'wide-thin' :
32687                     
32688                     box.push(item);
32689                     
32690                     break;
32691                 default :
32692                     break;
32693                     
32694             }
32695             
32696         }, this);
32697         
32698         if(box.length){
32699             boxes.push(box);
32700             box = [];
32701         }
32702         
32703         var filterPattern = function(box, length)
32704         {
32705             if(!box.length){
32706                 return;
32707             }
32708             
32709             var match = false;
32710             
32711             var pattern = box.slice(0, length);
32712             
32713             var format = [];
32714             
32715             Roo.each(pattern, function(i){
32716                 format.push(i.size);
32717             }, this);
32718             
32719             Roo.each(standard, function(s){
32720                 
32721                 if(String(s) != String(format)){
32722                     return;
32723                 }
32724                 
32725                 match = true;
32726                 return false;
32727                 
32728             }, this);
32729             
32730             if(!match && length == 1){
32731                 return;
32732             }
32733             
32734             if(!match){
32735                 filterPattern(box, length - 1);
32736                 return;
32737             }
32738                 
32739             queue.push(pattern);
32740
32741             box = box.slice(length, box.length);
32742
32743             filterPattern(box, 4);
32744
32745             return;
32746             
32747         }
32748         
32749         Roo.each(boxes, function(box, k){
32750             
32751             if(!box.length){
32752                 return;
32753             }
32754             
32755             if(box.length == 1){
32756                 queue.push(box);
32757                 return;
32758             }
32759             
32760             filterPattern(box, 4);
32761             
32762         }, this);
32763         
32764         
32765         var prune = [];
32766         
32767         var pos = this.el.getBox(true);
32768         
32769         var minX = pos.x;
32770         
32771         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32772         
32773         var hit_end = false;
32774         
32775         Roo.each(queue, function(box){
32776             
32777             if(hit_end){
32778                 
32779                 Roo.each(box, function(b){
32780                 
32781                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32782                     b.el.hide();
32783
32784                 }, this);
32785
32786                 return;
32787             }
32788             
32789             var mx = 0;
32790             
32791             Roo.each(box, function(b){
32792                 
32793                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32794                 b.el.show();
32795
32796                 mx = Math.max(mx, b.x);
32797                 
32798             }, this);
32799             
32800             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32801             
32802             if(maxX < minX){
32803                 
32804                 Roo.each(box, function(b){
32805                 
32806                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32807                     b.el.hide();
32808                     
32809                 }, this);
32810                 
32811                 hit_end = true;
32812                 
32813                 return;
32814             }
32815             
32816             prune.push(box);
32817             
32818         }, this);
32819         
32820         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32821     },
32822     
32823     /** Sets position of item in DOM
32824     * @param {Element} item
32825     * @param {Number} x - horizontal position
32826     * @param {Number} y - vertical position
32827     * @param {Boolean} isInstant - disables transitions
32828     */
32829     _processVerticalLayoutQueue : function( queue, isInstant )
32830     {
32831         var pos = this.el.getBox(true);
32832         var x = pos.x;
32833         var y = pos.y;
32834         var maxY = [];
32835         
32836         for (var i = 0; i < this.cols; i++){
32837             maxY[i] = pos.y;
32838         }
32839         
32840         Roo.each(queue, function(box, k){
32841             
32842             var col = k % this.cols;
32843             
32844             Roo.each(box, function(b,kk){
32845                 
32846                 b.el.position('absolute');
32847                 
32848                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32849                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32850                 
32851                 if(b.size == 'md-left' || b.size == 'md-right'){
32852                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32853                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32854                 }
32855                 
32856                 b.el.setWidth(width);
32857                 b.el.setHeight(height);
32858                 // iframe?
32859                 b.el.select('iframe',true).setSize(width,height);
32860                 
32861             }, this);
32862             
32863             for (var i = 0; i < this.cols; i++){
32864                 
32865                 if(maxY[i] < maxY[col]){
32866                     col = i;
32867                     continue;
32868                 }
32869                 
32870                 col = Math.min(col, i);
32871                 
32872             }
32873             
32874             x = pos.x + col * (this.colWidth + this.padWidth);
32875             
32876             y = maxY[col];
32877             
32878             var positions = [];
32879             
32880             switch (box.length){
32881                 case 1 :
32882                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32883                     break;
32884                 case 2 :
32885                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32886                     break;
32887                 case 3 :
32888                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32889                     break;
32890                 case 4 :
32891                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32892                     break;
32893                 default :
32894                     break;
32895             }
32896             
32897             Roo.each(box, function(b,kk){
32898                 
32899                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32900                 
32901                 var sz = b.el.getSize();
32902                 
32903                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32904                 
32905             }, this);
32906             
32907         }, this);
32908         
32909         var mY = 0;
32910         
32911         for (var i = 0; i < this.cols; i++){
32912             mY = Math.max(mY, maxY[i]);
32913         }
32914         
32915         this.el.setHeight(mY - pos.y);
32916         
32917     },
32918     
32919 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32920 //    {
32921 //        var pos = this.el.getBox(true);
32922 //        var x = pos.x;
32923 //        var y = pos.y;
32924 //        var maxX = pos.right;
32925 //        
32926 //        var maxHeight = 0;
32927 //        
32928 //        Roo.each(items, function(item, k){
32929 //            
32930 //            var c = k % 2;
32931 //            
32932 //            item.el.position('absolute');
32933 //                
32934 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32935 //
32936 //            item.el.setWidth(width);
32937 //
32938 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32939 //
32940 //            item.el.setHeight(height);
32941 //            
32942 //            if(c == 0){
32943 //                item.el.setXY([x, y], isInstant ? false : true);
32944 //            } else {
32945 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32946 //            }
32947 //            
32948 //            y = y + height + this.alternativePadWidth;
32949 //            
32950 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32951 //            
32952 //        }, this);
32953 //        
32954 //        this.el.setHeight(maxHeight);
32955 //        
32956 //    },
32957     
32958     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32959     {
32960         var pos = this.el.getBox(true);
32961         
32962         var minX = pos.x;
32963         var minY = pos.y;
32964         
32965         var maxX = pos.right;
32966         
32967         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32968         
32969         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32970         
32971         Roo.each(queue, function(box, k){
32972             
32973             Roo.each(box, function(b, kk){
32974                 
32975                 b.el.position('absolute');
32976                 
32977                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32978                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32979                 
32980                 if(b.size == 'md-left' || b.size == 'md-right'){
32981                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32982                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32983                 }
32984                 
32985                 b.el.setWidth(width);
32986                 b.el.setHeight(height);
32987                 
32988             }, this);
32989             
32990             if(!box.length){
32991                 return;
32992             }
32993             
32994             var positions = [];
32995             
32996             switch (box.length){
32997                 case 1 :
32998                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32999                     break;
33000                 case 2 :
33001                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33002                     break;
33003                 case 3 :
33004                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33005                     break;
33006                 case 4 :
33007                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33008                     break;
33009                 default :
33010                     break;
33011             }
33012             
33013             Roo.each(box, function(b,kk){
33014                 
33015                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33016                 
33017                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33018                 
33019             }, this);
33020             
33021         }, this);
33022         
33023     },
33024     
33025     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33026     {
33027         Roo.each(eItems, function(b,k){
33028             
33029             b.size = (k == 0) ? 'sm' : 'xs';
33030             b.x = (k == 0) ? 2 : 1;
33031             b.y = (k == 0) ? 2 : 1;
33032             
33033             b.el.position('absolute');
33034             
33035             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33036                 
33037             b.el.setWidth(width);
33038             
33039             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33040             
33041             b.el.setHeight(height);
33042             
33043         }, this);
33044
33045         var positions = [];
33046         
33047         positions.push({
33048             x : maxX - this.unitWidth * 2 - this.gutter,
33049             y : minY
33050         });
33051         
33052         positions.push({
33053             x : maxX - this.unitWidth,
33054             y : minY + (this.unitWidth + this.gutter) * 2
33055         });
33056         
33057         positions.push({
33058             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33059             y : minY
33060         });
33061         
33062         Roo.each(eItems, function(b,k){
33063             
33064             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33065
33066         }, this);
33067         
33068     },
33069     
33070     getVerticalOneBoxColPositions : function(x, y, box)
33071     {
33072         var pos = [];
33073         
33074         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33075         
33076         if(box[0].size == 'md-left'){
33077             rand = 0;
33078         }
33079         
33080         if(box[0].size == 'md-right'){
33081             rand = 1;
33082         }
33083         
33084         pos.push({
33085             x : x + (this.unitWidth + this.gutter) * rand,
33086             y : y
33087         });
33088         
33089         return pos;
33090     },
33091     
33092     getVerticalTwoBoxColPositions : function(x, y, box)
33093     {
33094         var pos = [];
33095         
33096         if(box[0].size == 'xs'){
33097             
33098             pos.push({
33099                 x : x,
33100                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33101             });
33102
33103             pos.push({
33104                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33105                 y : y
33106             });
33107             
33108             return pos;
33109             
33110         }
33111         
33112         pos.push({
33113             x : x,
33114             y : y
33115         });
33116
33117         pos.push({
33118             x : x + (this.unitWidth + this.gutter) * 2,
33119             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33120         });
33121         
33122         return pos;
33123         
33124     },
33125     
33126     getVerticalThreeBoxColPositions : function(x, y, box)
33127     {
33128         var pos = [];
33129         
33130         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33131             
33132             pos.push({
33133                 x : x,
33134                 y : y
33135             });
33136
33137             pos.push({
33138                 x : x + (this.unitWidth + this.gutter) * 1,
33139                 y : y
33140             });
33141             
33142             pos.push({
33143                 x : x + (this.unitWidth + this.gutter) * 2,
33144                 y : y
33145             });
33146             
33147             return pos;
33148             
33149         }
33150         
33151         if(box[0].size == 'xs' && box[1].size == 'xs'){
33152             
33153             pos.push({
33154                 x : x,
33155                 y : y
33156             });
33157
33158             pos.push({
33159                 x : x,
33160                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33161             });
33162             
33163             pos.push({
33164                 x : x + (this.unitWidth + this.gutter) * 1,
33165                 y : y
33166             });
33167             
33168             return pos;
33169             
33170         }
33171         
33172         pos.push({
33173             x : x,
33174             y : y
33175         });
33176
33177         pos.push({
33178             x : x + (this.unitWidth + this.gutter) * 2,
33179             y : y
33180         });
33181
33182         pos.push({
33183             x : x + (this.unitWidth + this.gutter) * 2,
33184             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33185         });
33186             
33187         return pos;
33188         
33189     },
33190     
33191     getVerticalFourBoxColPositions : function(x, y, box)
33192     {
33193         var pos = [];
33194         
33195         if(box[0].size == 'xs'){
33196             
33197             pos.push({
33198                 x : x,
33199                 y : y
33200             });
33201
33202             pos.push({
33203                 x : x,
33204                 y : y + (this.unitHeight + this.gutter) * 1
33205             });
33206             
33207             pos.push({
33208                 x : x,
33209                 y : y + (this.unitHeight + this.gutter) * 2
33210             });
33211             
33212             pos.push({
33213                 x : x + (this.unitWidth + this.gutter) * 1,
33214                 y : y
33215             });
33216             
33217             return pos;
33218             
33219         }
33220         
33221         pos.push({
33222             x : x,
33223             y : y
33224         });
33225
33226         pos.push({
33227             x : x + (this.unitWidth + this.gutter) * 2,
33228             y : y
33229         });
33230
33231         pos.push({
33232             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33233             y : y + (this.unitHeight + this.gutter) * 1
33234         });
33235
33236         pos.push({
33237             x : x + (this.unitWidth + this.gutter) * 2,
33238             y : y + (this.unitWidth + this.gutter) * 2
33239         });
33240
33241         return pos;
33242         
33243     },
33244     
33245     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33246     {
33247         var pos = [];
33248         
33249         if(box[0].size == 'md-left'){
33250             pos.push({
33251                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33252                 y : minY
33253             });
33254             
33255             return pos;
33256         }
33257         
33258         if(box[0].size == 'md-right'){
33259             pos.push({
33260                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33261                 y : minY + (this.unitWidth + this.gutter) * 1
33262             });
33263             
33264             return pos;
33265         }
33266         
33267         var rand = Math.floor(Math.random() * (4 - box[0].y));
33268         
33269         pos.push({
33270             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33271             y : minY + (this.unitWidth + this.gutter) * rand
33272         });
33273         
33274         return pos;
33275         
33276     },
33277     
33278     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33279     {
33280         var pos = [];
33281         
33282         if(box[0].size == 'xs'){
33283             
33284             pos.push({
33285                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33286                 y : minY
33287             });
33288
33289             pos.push({
33290                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33291                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33292             });
33293             
33294             return pos;
33295             
33296         }
33297         
33298         pos.push({
33299             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33300             y : minY
33301         });
33302
33303         pos.push({
33304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33305             y : minY + (this.unitWidth + this.gutter) * 2
33306         });
33307         
33308         return pos;
33309         
33310     },
33311     
33312     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33313     {
33314         var pos = [];
33315         
33316         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33317             
33318             pos.push({
33319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33320                 y : minY
33321             });
33322
33323             pos.push({
33324                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33325                 y : minY + (this.unitWidth + this.gutter) * 1
33326             });
33327             
33328             pos.push({
33329                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33330                 y : minY + (this.unitWidth + this.gutter) * 2
33331             });
33332             
33333             return pos;
33334             
33335         }
33336         
33337         if(box[0].size == 'xs' && box[1].size == 'xs'){
33338             
33339             pos.push({
33340                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33341                 y : minY
33342             });
33343
33344             pos.push({
33345                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33346                 y : minY
33347             });
33348             
33349             pos.push({
33350                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33351                 y : minY + (this.unitWidth + this.gutter) * 1
33352             });
33353             
33354             return pos;
33355             
33356         }
33357         
33358         pos.push({
33359             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33360             y : minY
33361         });
33362
33363         pos.push({
33364             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33365             y : minY + (this.unitWidth + this.gutter) * 2
33366         });
33367
33368         pos.push({
33369             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33370             y : minY + (this.unitWidth + this.gutter) * 2
33371         });
33372             
33373         return pos;
33374         
33375     },
33376     
33377     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33378     {
33379         var pos = [];
33380         
33381         if(box[0].size == 'xs'){
33382             
33383             pos.push({
33384                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33385                 y : minY
33386             });
33387
33388             pos.push({
33389                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33390                 y : minY
33391             });
33392             
33393             pos.push({
33394                 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),
33395                 y : minY
33396             });
33397             
33398             pos.push({
33399                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33400                 y : minY + (this.unitWidth + this.gutter) * 1
33401             });
33402             
33403             return pos;
33404             
33405         }
33406         
33407         pos.push({
33408             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33409             y : minY
33410         });
33411         
33412         pos.push({
33413             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33414             y : minY + (this.unitWidth + this.gutter) * 2
33415         });
33416         
33417         pos.push({
33418             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33419             y : minY + (this.unitWidth + this.gutter) * 2
33420         });
33421         
33422         pos.push({
33423             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),
33424             y : minY + (this.unitWidth + this.gutter) * 2
33425         });
33426
33427         return pos;
33428         
33429     },
33430     
33431     /**
33432     * remove a Masonry Brick
33433     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33434     */
33435     removeBrick : function(brick_id)
33436     {
33437         if (!brick_id) {
33438             return;
33439         }
33440         
33441         for (var i = 0; i<this.bricks.length; i++) {
33442             if (this.bricks[i].id == brick_id) {
33443                 this.bricks.splice(i,1);
33444                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33445                 this.initial();
33446             }
33447         }
33448     },
33449     
33450     /**
33451     * adds a Masonry Brick
33452     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33453     */
33454     addBrick : function(cfg)
33455     {
33456         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33457         //this.register(cn);
33458         cn.parentId = this.id;
33459         cn.render(this.el);
33460         return cn;
33461     },
33462     
33463     /**
33464     * register a Masonry Brick
33465     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33466     */
33467     
33468     register : function(brick)
33469     {
33470         this.bricks.push(brick);
33471         brick.masonryId = this.id;
33472     },
33473     
33474     /**
33475     * clear all the Masonry Brick
33476     */
33477     clearAll : function()
33478     {
33479         this.bricks = [];
33480         //this.getChildContainer().dom.innerHTML = "";
33481         this.el.dom.innerHTML = '';
33482     },
33483     
33484     getSelected : function()
33485     {
33486         if (!this.selectedBrick) {
33487             return false;
33488         }
33489         
33490         return this.selectedBrick;
33491     }
33492 });
33493
33494 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33495     
33496     groups: {},
33497      /**
33498     * register a Masonry Layout
33499     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33500     */
33501     
33502     register : function(layout)
33503     {
33504         this.groups[layout.id] = layout;
33505     },
33506     /**
33507     * fetch a  Masonry Layout based on the masonry layout ID
33508     * @param {string} the masonry layout to add
33509     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33510     */
33511     
33512     get: function(layout_id) {
33513         if (typeof(this.groups[layout_id]) == 'undefined') {
33514             return false;
33515         }
33516         return this.groups[layout_id] ;
33517     }
33518     
33519     
33520     
33521 });
33522
33523  
33524
33525  /**
33526  *
33527  * This is based on 
33528  * http://masonry.desandro.com
33529  *
33530  * The idea is to render all the bricks based on vertical width...
33531  *
33532  * The original code extends 'outlayer' - we might need to use that....
33533  * 
33534  */
33535
33536
33537 /**
33538  * @class Roo.bootstrap.LayoutMasonryAuto
33539  * @extends Roo.bootstrap.Component
33540  * Bootstrap Layout Masonry class
33541  * 
33542  * @constructor
33543  * Create a new Element
33544  * @param {Object} config The config object
33545  */
33546
33547 Roo.bootstrap.LayoutMasonryAuto = function(config){
33548     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33549 };
33550
33551 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33552     
33553       /**
33554      * @cfg {Boolean} isFitWidth  - resize the width..
33555      */   
33556     isFitWidth : false,  // options..
33557     /**
33558      * @cfg {Boolean} isOriginLeft = left align?
33559      */   
33560     isOriginLeft : true,
33561     /**
33562      * @cfg {Boolean} isOriginTop = top align?
33563      */   
33564     isOriginTop : false,
33565     /**
33566      * @cfg {Boolean} isLayoutInstant = no animation?
33567      */   
33568     isLayoutInstant : false, // needed?
33569     /**
33570      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33571      */   
33572     isResizingContainer : true,
33573     /**
33574      * @cfg {Number} columnWidth  width of the columns 
33575      */   
33576     
33577     columnWidth : 0,
33578     
33579     /**
33580      * @cfg {Number} maxCols maximum number of columns
33581      */   
33582     
33583     maxCols: 0,
33584     /**
33585      * @cfg {Number} padHeight padding below box..
33586      */   
33587     
33588     padHeight : 10, 
33589     
33590     /**
33591      * @cfg {Boolean} isAutoInitial defalut true
33592      */   
33593     
33594     isAutoInitial : true, 
33595     
33596     // private?
33597     gutter : 0,
33598     
33599     containerWidth: 0,
33600     initialColumnWidth : 0,
33601     currentSize : null,
33602     
33603     colYs : null, // array.
33604     maxY : 0,
33605     padWidth: 10,
33606     
33607     
33608     tag: 'div',
33609     cls: '',
33610     bricks: null, //CompositeElement
33611     cols : 0, // array?
33612     // element : null, // wrapped now this.el
33613     _isLayoutInited : null, 
33614     
33615     
33616     getAutoCreate : function(){
33617         
33618         var cfg = {
33619             tag: this.tag,
33620             cls: 'blog-masonary-wrapper ' + this.cls,
33621             cn : {
33622                 cls : 'mas-boxes masonary'
33623             }
33624         };
33625         
33626         return cfg;
33627     },
33628     
33629     getChildContainer: function( )
33630     {
33631         if (this.boxesEl) {
33632             return this.boxesEl;
33633         }
33634         
33635         this.boxesEl = this.el.select('.mas-boxes').first();
33636         
33637         return this.boxesEl;
33638     },
33639     
33640     
33641     initEvents : function()
33642     {
33643         var _this = this;
33644         
33645         if(this.isAutoInitial){
33646             Roo.log('hook children rendered');
33647             this.on('childrenrendered', function() {
33648                 Roo.log('children rendered');
33649                 _this.initial();
33650             } ,this);
33651         }
33652         
33653     },
33654     
33655     initial : function()
33656     {
33657         this.reloadItems();
33658
33659         this.currentSize = this.el.getBox(true);
33660
33661         /// was window resize... - let's see if this works..
33662         Roo.EventManager.onWindowResize(this.resize, this); 
33663
33664         if(!this.isAutoInitial){
33665             this.layout();
33666             return;
33667         }
33668         
33669         this.layout.defer(500,this);
33670     },
33671     
33672     reloadItems: function()
33673     {
33674         this.bricks = this.el.select('.masonry-brick', true);
33675         
33676         this.bricks.each(function(b) {
33677             //Roo.log(b.getSize());
33678             if (!b.attr('originalwidth')) {
33679                 b.attr('originalwidth',  b.getSize().width);
33680             }
33681             
33682         });
33683         
33684         Roo.log(this.bricks.elements.length);
33685     },
33686     
33687     resize : function()
33688     {
33689         Roo.log('resize');
33690         var cs = this.el.getBox(true);
33691         
33692         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33693             Roo.log("no change in with or X");
33694             return;
33695         }
33696         this.currentSize = cs;
33697         this.layout();
33698     },
33699     
33700     layout : function()
33701     {
33702          Roo.log('layout');
33703         this._resetLayout();
33704         //this._manageStamps();
33705       
33706         // don't animate first layout
33707         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33708         this.layoutItems( isInstant );
33709       
33710         // flag for initalized
33711         this._isLayoutInited = true;
33712     },
33713     
33714     layoutItems : function( isInstant )
33715     {
33716         //var items = this._getItemsForLayout( this.items );
33717         // original code supports filtering layout items.. we just ignore it..
33718         
33719         this._layoutItems( this.bricks , isInstant );
33720       
33721         this._postLayout();
33722     },
33723     _layoutItems : function ( items , isInstant)
33724     {
33725        //this.fireEvent( 'layout', this, items );
33726     
33727
33728         if ( !items || !items.elements.length ) {
33729           // no items, emit event with empty array
33730             return;
33731         }
33732
33733         var queue = [];
33734         items.each(function(item) {
33735             Roo.log("layout item");
33736             Roo.log(item);
33737             // get x/y object from method
33738             var position = this._getItemLayoutPosition( item );
33739             // enqueue
33740             position.item = item;
33741             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33742             queue.push( position );
33743         }, this);
33744       
33745         this._processLayoutQueue( queue );
33746     },
33747     /** Sets position of item in DOM
33748     * @param {Element} item
33749     * @param {Number} x - horizontal position
33750     * @param {Number} y - vertical position
33751     * @param {Boolean} isInstant - disables transitions
33752     */
33753     _processLayoutQueue : function( queue )
33754     {
33755         for ( var i=0, len = queue.length; i < len; i++ ) {
33756             var obj = queue[i];
33757             obj.item.position('absolute');
33758             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33759         }
33760     },
33761       
33762     
33763     /**
33764     * Any logic you want to do after each layout,
33765     * i.e. size the container
33766     */
33767     _postLayout : function()
33768     {
33769         this.resizeContainer();
33770     },
33771     
33772     resizeContainer : function()
33773     {
33774         if ( !this.isResizingContainer ) {
33775             return;
33776         }
33777         var size = this._getContainerSize();
33778         if ( size ) {
33779             this.el.setSize(size.width,size.height);
33780             this.boxesEl.setSize(size.width,size.height);
33781         }
33782     },
33783     
33784     
33785     
33786     _resetLayout : function()
33787     {
33788         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33789         this.colWidth = this.el.getWidth();
33790         //this.gutter = this.el.getWidth(); 
33791         
33792         this.measureColumns();
33793
33794         // reset column Y
33795         var i = this.cols;
33796         this.colYs = [];
33797         while (i--) {
33798             this.colYs.push( 0 );
33799         }
33800     
33801         this.maxY = 0;
33802     },
33803
33804     measureColumns : function()
33805     {
33806         this.getContainerWidth();
33807       // if columnWidth is 0, default to outerWidth of first item
33808         if ( !this.columnWidth ) {
33809             var firstItem = this.bricks.first();
33810             Roo.log(firstItem);
33811             this.columnWidth  = this.containerWidth;
33812             if (firstItem && firstItem.attr('originalwidth') ) {
33813                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33814             }
33815             // columnWidth fall back to item of first element
33816             Roo.log("set column width?");
33817                         this.initialColumnWidth = this.columnWidth  ;
33818
33819             // if first elem has no width, default to size of container
33820             
33821         }
33822         
33823         
33824         if (this.initialColumnWidth) {
33825             this.columnWidth = this.initialColumnWidth;
33826         }
33827         
33828         
33829             
33830         // column width is fixed at the top - however if container width get's smaller we should
33831         // reduce it...
33832         
33833         // this bit calcs how man columns..
33834             
33835         var columnWidth = this.columnWidth += this.gutter;
33836       
33837         // calculate columns
33838         var containerWidth = this.containerWidth + this.gutter;
33839         
33840         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33841         // fix rounding errors, typically with gutters
33842         var excess = columnWidth - containerWidth % columnWidth;
33843         
33844         
33845         // if overshoot is less than a pixel, round up, otherwise floor it
33846         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33847         cols = Math[ mathMethod ]( cols );
33848         this.cols = Math.max( cols, 1 );
33849         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33850         
33851          // padding positioning..
33852         var totalColWidth = this.cols * this.columnWidth;
33853         var padavail = this.containerWidth - totalColWidth;
33854         // so for 2 columns - we need 3 'pads'
33855         
33856         var padNeeded = (1+this.cols) * this.padWidth;
33857         
33858         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33859         
33860         this.columnWidth += padExtra
33861         //this.padWidth = Math.floor(padavail /  ( this.cols));
33862         
33863         // adjust colum width so that padding is fixed??
33864         
33865         // we have 3 columns ... total = width * 3
33866         // we have X left over... that should be used by 
33867         
33868         //if (this.expandC) {
33869             
33870         //}
33871         
33872         
33873         
33874     },
33875     
33876     getContainerWidth : function()
33877     {
33878        /* // container is parent if fit width
33879         var container = this.isFitWidth ? this.element.parentNode : this.element;
33880         // check that this.size and size are there
33881         // IE8 triggers resize on body size change, so they might not be
33882         
33883         var size = getSize( container );  //FIXME
33884         this.containerWidth = size && size.innerWidth; //FIXME
33885         */
33886          
33887         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33888         
33889     },
33890     
33891     _getItemLayoutPosition : function( item )  // what is item?
33892     {
33893         // we resize the item to our columnWidth..
33894       
33895         item.setWidth(this.columnWidth);
33896         item.autoBoxAdjust  = false;
33897         
33898         var sz = item.getSize();
33899  
33900         // how many columns does this brick span
33901         var remainder = this.containerWidth % this.columnWidth;
33902         
33903         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33904         // round if off by 1 pixel, otherwise use ceil
33905         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33906         colSpan = Math.min( colSpan, this.cols );
33907         
33908         // normally this should be '1' as we dont' currently allow multi width columns..
33909         
33910         var colGroup = this._getColGroup( colSpan );
33911         // get the minimum Y value from the columns
33912         var minimumY = Math.min.apply( Math, colGroup );
33913         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33914         
33915         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33916          
33917         // position the brick
33918         var position = {
33919             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33920             y: this.currentSize.y + minimumY + this.padHeight
33921         };
33922         
33923         Roo.log(position);
33924         // apply setHeight to necessary columns
33925         var setHeight = minimumY + sz.height + this.padHeight;
33926         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33927         
33928         var setSpan = this.cols + 1 - colGroup.length;
33929         for ( var i = 0; i < setSpan; i++ ) {
33930           this.colYs[ shortColIndex + i ] = setHeight ;
33931         }
33932       
33933         return position;
33934     },
33935     
33936     /**
33937      * @param {Number} colSpan - number of columns the element spans
33938      * @returns {Array} colGroup
33939      */
33940     _getColGroup : function( colSpan )
33941     {
33942         if ( colSpan < 2 ) {
33943           // if brick spans only one column, use all the column Ys
33944           return this.colYs;
33945         }
33946       
33947         var colGroup = [];
33948         // how many different places could this brick fit horizontally
33949         var groupCount = this.cols + 1 - colSpan;
33950         // for each group potential horizontal position
33951         for ( var i = 0; i < groupCount; i++ ) {
33952           // make an array of colY values for that one group
33953           var groupColYs = this.colYs.slice( i, i + colSpan );
33954           // and get the max value of the array
33955           colGroup[i] = Math.max.apply( Math, groupColYs );
33956         }
33957         return colGroup;
33958     },
33959     /*
33960     _manageStamp : function( stamp )
33961     {
33962         var stampSize =  stamp.getSize();
33963         var offset = stamp.getBox();
33964         // get the columns that this stamp affects
33965         var firstX = this.isOriginLeft ? offset.x : offset.right;
33966         var lastX = firstX + stampSize.width;
33967         var firstCol = Math.floor( firstX / this.columnWidth );
33968         firstCol = Math.max( 0, firstCol );
33969         
33970         var lastCol = Math.floor( lastX / this.columnWidth );
33971         // lastCol should not go over if multiple of columnWidth #425
33972         lastCol -= lastX % this.columnWidth ? 0 : 1;
33973         lastCol = Math.min( this.cols - 1, lastCol );
33974         
33975         // set colYs to bottom of the stamp
33976         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33977             stampSize.height;
33978             
33979         for ( var i = firstCol; i <= lastCol; i++ ) {
33980           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33981         }
33982     },
33983     */
33984     
33985     _getContainerSize : function()
33986     {
33987         this.maxY = Math.max.apply( Math, this.colYs );
33988         var size = {
33989             height: this.maxY
33990         };
33991       
33992         if ( this.isFitWidth ) {
33993             size.width = this._getContainerFitWidth();
33994         }
33995       
33996         return size;
33997     },
33998     
33999     _getContainerFitWidth : function()
34000     {
34001         var unusedCols = 0;
34002         // count unused columns
34003         var i = this.cols;
34004         while ( --i ) {
34005           if ( this.colYs[i] !== 0 ) {
34006             break;
34007           }
34008           unusedCols++;
34009         }
34010         // fit container to columns that have been used
34011         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34012     },
34013     
34014     needsResizeLayout : function()
34015     {
34016         var previousWidth = this.containerWidth;
34017         this.getContainerWidth();
34018         return previousWidth !== this.containerWidth;
34019     }
34020  
34021 });
34022
34023  
34024
34025  /*
34026  * - LGPL
34027  *
34028  * element
34029  * 
34030  */
34031
34032 /**
34033  * @class Roo.bootstrap.MasonryBrick
34034  * @extends Roo.bootstrap.Component
34035  * Bootstrap MasonryBrick class
34036  * 
34037  * @constructor
34038  * Create a new MasonryBrick
34039  * @param {Object} config The config object
34040  */
34041
34042 Roo.bootstrap.MasonryBrick = function(config){
34043     
34044     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34045     
34046     Roo.bootstrap.MasonryBrick.register(this);
34047     
34048     this.addEvents({
34049         // raw events
34050         /**
34051          * @event click
34052          * When a MasonryBrick is clcik
34053          * @param {Roo.bootstrap.MasonryBrick} this
34054          * @param {Roo.EventObject} e
34055          */
34056         "click" : true
34057     });
34058 };
34059
34060 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34061     
34062     /**
34063      * @cfg {String} title
34064      */   
34065     title : '',
34066     /**
34067      * @cfg {String} html
34068      */   
34069     html : '',
34070     /**
34071      * @cfg {String} bgimage
34072      */   
34073     bgimage : '',
34074     /**
34075      * @cfg {String} videourl
34076      */   
34077     videourl : '',
34078     /**
34079      * @cfg {String} cls
34080      */   
34081     cls : '',
34082     /**
34083      * @cfg {String} href
34084      */   
34085     href : '',
34086     /**
34087      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34088      */   
34089     size : 'xs',
34090     
34091     /**
34092      * @cfg {String} placetitle (center|bottom)
34093      */   
34094     placetitle : '',
34095     
34096     /**
34097      * @cfg {Boolean} isFitContainer defalut true
34098      */   
34099     isFitContainer : true, 
34100     
34101     /**
34102      * @cfg {Boolean} preventDefault defalut false
34103      */   
34104     preventDefault : false, 
34105     
34106     /**
34107      * @cfg {Boolean} inverse defalut false
34108      */   
34109     maskInverse : false, 
34110     
34111     getAutoCreate : function()
34112     {
34113         if(!this.isFitContainer){
34114             return this.getSplitAutoCreate();
34115         }
34116         
34117         var cls = 'masonry-brick masonry-brick-full';
34118         
34119         if(this.href.length){
34120             cls += ' masonry-brick-link';
34121         }
34122         
34123         if(this.bgimage.length){
34124             cls += ' masonry-brick-image';
34125         }
34126         
34127         if(this.maskInverse){
34128             cls += ' mask-inverse';
34129         }
34130         
34131         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34132             cls += ' enable-mask';
34133         }
34134         
34135         if(this.size){
34136             cls += ' masonry-' + this.size + '-brick';
34137         }
34138         
34139         if(this.placetitle.length){
34140             
34141             switch (this.placetitle) {
34142                 case 'center' :
34143                     cls += ' masonry-center-title';
34144                     break;
34145                 case 'bottom' :
34146                     cls += ' masonry-bottom-title';
34147                     break;
34148                 default:
34149                     break;
34150             }
34151             
34152         } else {
34153             if(!this.html.length && !this.bgimage.length){
34154                 cls += ' masonry-center-title';
34155             }
34156
34157             if(!this.html.length && this.bgimage.length){
34158                 cls += ' masonry-bottom-title';
34159             }
34160         }
34161         
34162         if(this.cls){
34163             cls += ' ' + this.cls;
34164         }
34165         
34166         var cfg = {
34167             tag: (this.href.length) ? 'a' : 'div',
34168             cls: cls,
34169             cn: [
34170                 {
34171                     tag: 'div',
34172                     cls: 'masonry-brick-mask'
34173                 },
34174                 {
34175                     tag: 'div',
34176                     cls: 'masonry-brick-paragraph',
34177                     cn: []
34178                 }
34179             ]
34180         };
34181         
34182         if(this.href.length){
34183             cfg.href = this.href;
34184         }
34185         
34186         var cn = cfg.cn[1].cn;
34187         
34188         if(this.title.length){
34189             cn.push({
34190                 tag: 'h4',
34191                 cls: 'masonry-brick-title',
34192                 html: this.title
34193             });
34194         }
34195         
34196         if(this.html.length){
34197             cn.push({
34198                 tag: 'p',
34199                 cls: 'masonry-brick-text',
34200                 html: this.html
34201             });
34202         }
34203         
34204         if (!this.title.length && !this.html.length) {
34205             cfg.cn[1].cls += ' hide';
34206         }
34207         
34208         if(this.bgimage.length){
34209             cfg.cn.push({
34210                 tag: 'img',
34211                 cls: 'masonry-brick-image-view',
34212                 src: this.bgimage
34213             });
34214         }
34215         
34216         if(this.videourl.length){
34217             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34218             // youtube support only?
34219             cfg.cn.push({
34220                 tag: 'iframe',
34221                 cls: 'masonry-brick-image-view',
34222                 src: vurl,
34223                 frameborder : 0,
34224                 allowfullscreen : true
34225             });
34226         }
34227         
34228         return cfg;
34229         
34230     },
34231     
34232     getSplitAutoCreate : function()
34233     {
34234         var cls = 'masonry-brick masonry-brick-split';
34235         
34236         if(this.href.length){
34237             cls += ' masonry-brick-link';
34238         }
34239         
34240         if(this.bgimage.length){
34241             cls += ' masonry-brick-image';
34242         }
34243         
34244         if(this.size){
34245             cls += ' masonry-' + this.size + '-brick';
34246         }
34247         
34248         switch (this.placetitle) {
34249             case 'center' :
34250                 cls += ' masonry-center-title';
34251                 break;
34252             case 'bottom' :
34253                 cls += ' masonry-bottom-title';
34254                 break;
34255             default:
34256                 if(!this.bgimage.length){
34257                     cls += ' masonry-center-title';
34258                 }
34259
34260                 if(this.bgimage.length){
34261                     cls += ' masonry-bottom-title';
34262                 }
34263                 break;
34264         }
34265         
34266         if(this.cls){
34267             cls += ' ' + this.cls;
34268         }
34269         
34270         var cfg = {
34271             tag: (this.href.length) ? 'a' : 'div',
34272             cls: cls,
34273             cn: [
34274                 {
34275                     tag: 'div',
34276                     cls: 'masonry-brick-split-head',
34277                     cn: [
34278                         {
34279                             tag: 'div',
34280                             cls: 'masonry-brick-paragraph',
34281                             cn: []
34282                         }
34283                     ]
34284                 },
34285                 {
34286                     tag: 'div',
34287                     cls: 'masonry-brick-split-body',
34288                     cn: []
34289                 }
34290             ]
34291         };
34292         
34293         if(this.href.length){
34294             cfg.href = this.href;
34295         }
34296         
34297         if(this.title.length){
34298             cfg.cn[0].cn[0].cn.push({
34299                 tag: 'h4',
34300                 cls: 'masonry-brick-title',
34301                 html: this.title
34302             });
34303         }
34304         
34305         if(this.html.length){
34306             cfg.cn[1].cn.push({
34307                 tag: 'p',
34308                 cls: 'masonry-brick-text',
34309                 html: this.html
34310             });
34311         }
34312
34313         if(this.bgimage.length){
34314             cfg.cn[0].cn.push({
34315                 tag: 'img',
34316                 cls: 'masonry-brick-image-view',
34317                 src: this.bgimage
34318             });
34319         }
34320         
34321         if(this.videourl.length){
34322             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34323             // youtube support only?
34324             cfg.cn[0].cn.cn.push({
34325                 tag: 'iframe',
34326                 cls: 'masonry-brick-image-view',
34327                 src: vurl,
34328                 frameborder : 0,
34329                 allowfullscreen : true
34330             });
34331         }
34332         
34333         return cfg;
34334     },
34335     
34336     initEvents: function() 
34337     {
34338         switch (this.size) {
34339             case 'xs' :
34340                 this.x = 1;
34341                 this.y = 1;
34342                 break;
34343             case 'sm' :
34344                 this.x = 2;
34345                 this.y = 2;
34346                 break;
34347             case 'md' :
34348             case 'md-left' :
34349             case 'md-right' :
34350                 this.x = 3;
34351                 this.y = 3;
34352                 break;
34353             case 'tall' :
34354                 this.x = 2;
34355                 this.y = 3;
34356                 break;
34357             case 'wide' :
34358                 this.x = 3;
34359                 this.y = 2;
34360                 break;
34361             case 'wide-thin' :
34362                 this.x = 3;
34363                 this.y = 1;
34364                 break;
34365                         
34366             default :
34367                 break;
34368         }
34369         
34370         if(Roo.isTouch){
34371             this.el.on('touchstart', this.onTouchStart, this);
34372             this.el.on('touchmove', this.onTouchMove, this);
34373             this.el.on('touchend', this.onTouchEnd, this);
34374             this.el.on('contextmenu', this.onContextMenu, this);
34375         } else {
34376             this.el.on('mouseenter'  ,this.enter, this);
34377             this.el.on('mouseleave', this.leave, this);
34378             this.el.on('click', this.onClick, this);
34379         }
34380         
34381         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34382             this.parent().bricks.push(this);   
34383         }
34384         
34385     },
34386     
34387     onClick: function(e, el)
34388     {
34389         var time = this.endTimer - this.startTimer;
34390         // Roo.log(e.preventDefault());
34391         if(Roo.isTouch){
34392             if(time > 1000){
34393                 e.preventDefault();
34394                 return;
34395             }
34396         }
34397         
34398         if(!this.preventDefault){
34399             return;
34400         }
34401         
34402         e.preventDefault();
34403         
34404         if (this.activeClass != '') {
34405             this.selectBrick();
34406         }
34407         
34408         this.fireEvent('click', this, e);
34409     },
34410     
34411     enter: function(e, el)
34412     {
34413         e.preventDefault();
34414         
34415         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34416             return;
34417         }
34418         
34419         if(this.bgimage.length && this.html.length){
34420             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34421         }
34422     },
34423     
34424     leave: function(e, el)
34425     {
34426         e.preventDefault();
34427         
34428         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34429             return;
34430         }
34431         
34432         if(this.bgimage.length && this.html.length){
34433             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34434         }
34435     },
34436     
34437     onTouchStart: function(e, el)
34438     {
34439 //        e.preventDefault();
34440         
34441         this.touchmoved = false;
34442         
34443         if(!this.isFitContainer){
34444             return;
34445         }
34446         
34447         if(!this.bgimage.length || !this.html.length){
34448             return;
34449         }
34450         
34451         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34452         
34453         this.timer = new Date().getTime();
34454         
34455     },
34456     
34457     onTouchMove: function(e, el)
34458     {
34459         this.touchmoved = true;
34460     },
34461     
34462     onContextMenu : function(e,el)
34463     {
34464         e.preventDefault();
34465         e.stopPropagation();
34466         return false;
34467     },
34468     
34469     onTouchEnd: function(e, el)
34470     {
34471 //        e.preventDefault();
34472         
34473         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34474         
34475             this.leave(e,el);
34476             
34477             return;
34478         }
34479         
34480         if(!this.bgimage.length || !this.html.length){
34481             
34482             if(this.href.length){
34483                 window.location.href = this.href;
34484             }
34485             
34486             return;
34487         }
34488         
34489         if(!this.isFitContainer){
34490             return;
34491         }
34492         
34493         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34494         
34495         window.location.href = this.href;
34496     },
34497     
34498     //selection on single brick only
34499     selectBrick : function() {
34500         
34501         if (!this.parentId) {
34502             return;
34503         }
34504         
34505         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34506         var index = m.selectedBrick.indexOf(this.id);
34507         
34508         if ( index > -1) {
34509             m.selectedBrick.splice(index,1);
34510             this.el.removeClass(this.activeClass);
34511             return;
34512         }
34513         
34514         for(var i = 0; i < m.selectedBrick.length; i++) {
34515             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34516             b.el.removeClass(b.activeClass);
34517         }
34518         
34519         m.selectedBrick = [];
34520         
34521         m.selectedBrick.push(this.id);
34522         this.el.addClass(this.activeClass);
34523         return;
34524     },
34525     
34526     isSelected : function(){
34527         return this.el.hasClass(this.activeClass);
34528         
34529     }
34530 });
34531
34532 Roo.apply(Roo.bootstrap.MasonryBrick, {
34533     
34534     //groups: {},
34535     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34536      /**
34537     * register a Masonry Brick
34538     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34539     */
34540     
34541     register : function(brick)
34542     {
34543         //this.groups[brick.id] = brick;
34544         this.groups.add(brick.id, brick);
34545     },
34546     /**
34547     * fetch a  masonry brick based on the masonry brick ID
34548     * @param {string} the masonry brick to add
34549     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34550     */
34551     
34552     get: function(brick_id) 
34553     {
34554         // if (typeof(this.groups[brick_id]) == 'undefined') {
34555         //     return false;
34556         // }
34557         // return this.groups[brick_id] ;
34558         
34559         if(this.groups.key(brick_id)) {
34560             return this.groups.key(brick_id);
34561         }
34562         
34563         return false;
34564     }
34565     
34566     
34567     
34568 });
34569
34570  /*
34571  * - LGPL
34572  *
34573  * element
34574  * 
34575  */
34576
34577 /**
34578  * @class Roo.bootstrap.Brick
34579  * @extends Roo.bootstrap.Component
34580  * Bootstrap Brick class
34581  * 
34582  * @constructor
34583  * Create a new Brick
34584  * @param {Object} config The config object
34585  */
34586
34587 Roo.bootstrap.Brick = function(config){
34588     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34589     
34590     this.addEvents({
34591         // raw events
34592         /**
34593          * @event click
34594          * When a Brick is click
34595          * @param {Roo.bootstrap.Brick} this
34596          * @param {Roo.EventObject} e
34597          */
34598         "click" : true
34599     });
34600 };
34601
34602 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34603     
34604     /**
34605      * @cfg {String} title
34606      */   
34607     title : '',
34608     /**
34609      * @cfg {String} html
34610      */   
34611     html : '',
34612     /**
34613      * @cfg {String} bgimage
34614      */   
34615     bgimage : '',
34616     /**
34617      * @cfg {String} cls
34618      */   
34619     cls : '',
34620     /**
34621      * @cfg {String} href
34622      */   
34623     href : '',
34624     /**
34625      * @cfg {String} video
34626      */   
34627     video : '',
34628     /**
34629      * @cfg {Boolean} square
34630      */   
34631     square : true,
34632     
34633     getAutoCreate : function()
34634     {
34635         var cls = 'roo-brick';
34636         
34637         if(this.href.length){
34638             cls += ' roo-brick-link';
34639         }
34640         
34641         if(this.bgimage.length){
34642             cls += ' roo-brick-image';
34643         }
34644         
34645         if(!this.html.length && !this.bgimage.length){
34646             cls += ' roo-brick-center-title';
34647         }
34648         
34649         if(!this.html.length && this.bgimage.length){
34650             cls += ' roo-brick-bottom-title';
34651         }
34652         
34653         if(this.cls){
34654             cls += ' ' + this.cls;
34655         }
34656         
34657         var cfg = {
34658             tag: (this.href.length) ? 'a' : 'div',
34659             cls: cls,
34660             cn: [
34661                 {
34662                     tag: 'div',
34663                     cls: 'roo-brick-paragraph',
34664                     cn: []
34665                 }
34666             ]
34667         };
34668         
34669         if(this.href.length){
34670             cfg.href = this.href;
34671         }
34672         
34673         var cn = cfg.cn[0].cn;
34674         
34675         if(this.title.length){
34676             cn.push({
34677                 tag: 'h4',
34678                 cls: 'roo-brick-title',
34679                 html: this.title
34680             });
34681         }
34682         
34683         if(this.html.length){
34684             cn.push({
34685                 tag: 'p',
34686                 cls: 'roo-brick-text',
34687                 html: this.html
34688             });
34689         } else {
34690             cn.cls += ' hide';
34691         }
34692         
34693         if(this.bgimage.length){
34694             cfg.cn.push({
34695                 tag: 'img',
34696                 cls: 'roo-brick-image-view',
34697                 src: this.bgimage
34698             });
34699         }
34700         
34701         return cfg;
34702     },
34703     
34704     initEvents: function() 
34705     {
34706         if(this.title.length || this.html.length){
34707             this.el.on('mouseenter'  ,this.enter, this);
34708             this.el.on('mouseleave', this.leave, this);
34709         }
34710         
34711         Roo.EventManager.onWindowResize(this.resize, this); 
34712         
34713         if(this.bgimage.length){
34714             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34715             this.imageEl.on('load', this.onImageLoad, this);
34716             return;
34717         }
34718         
34719         this.resize();
34720     },
34721     
34722     onImageLoad : function()
34723     {
34724         this.resize();
34725     },
34726     
34727     resize : function()
34728     {
34729         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34730         
34731         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34732         
34733         if(this.bgimage.length){
34734             var image = this.el.select('.roo-brick-image-view', true).first();
34735             
34736             image.setWidth(paragraph.getWidth());
34737             
34738             if(this.square){
34739                 image.setHeight(paragraph.getWidth());
34740             }
34741             
34742             this.el.setHeight(image.getHeight());
34743             paragraph.setHeight(image.getHeight());
34744             
34745         }
34746         
34747     },
34748     
34749     enter: function(e, el)
34750     {
34751         e.preventDefault();
34752         
34753         if(this.bgimage.length){
34754             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34755             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34756         }
34757     },
34758     
34759     leave: function(e, el)
34760     {
34761         e.preventDefault();
34762         
34763         if(this.bgimage.length){
34764             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34765             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34766         }
34767     }
34768     
34769 });
34770
34771  
34772
34773  /*
34774  * - LGPL
34775  *
34776  * Number field 
34777  */
34778
34779 /**
34780  * @class Roo.bootstrap.NumberField
34781  * @extends Roo.bootstrap.Input
34782  * Bootstrap NumberField class
34783  * 
34784  * 
34785  * 
34786  * 
34787  * @constructor
34788  * Create a new NumberField
34789  * @param {Object} config The config object
34790  */
34791
34792 Roo.bootstrap.NumberField = function(config){
34793     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34794 };
34795
34796 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34797     
34798     /**
34799      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34800      */
34801     allowDecimals : true,
34802     /**
34803      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34804      */
34805     decimalSeparator : ".",
34806     /**
34807      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34808      */
34809     decimalPrecision : 2,
34810     /**
34811      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34812      */
34813     allowNegative : true,
34814     
34815     /**
34816      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34817      */
34818     allowZero: true,
34819     /**
34820      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34821      */
34822     minValue : Number.NEGATIVE_INFINITY,
34823     /**
34824      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34825      */
34826     maxValue : Number.MAX_VALUE,
34827     /**
34828      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34829      */
34830     minText : "The minimum value for this field is {0}",
34831     /**
34832      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34833      */
34834     maxText : "The maximum value for this field is {0}",
34835     /**
34836      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34837      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34838      */
34839     nanText : "{0} is not a valid number",
34840     /**
34841      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34842      */
34843     thousandsDelimiter : false,
34844     /**
34845      * @cfg {String} valueAlign alignment of value
34846      */
34847     valueAlign : "left",
34848
34849     getAutoCreate : function()
34850     {
34851         var hiddenInput = {
34852             tag: 'input',
34853             type: 'hidden',
34854             id: Roo.id(),
34855             cls: 'hidden-number-input'
34856         };
34857         
34858         if (this.name) {
34859             hiddenInput.name = this.name;
34860         }
34861         
34862         this.name = '';
34863         
34864         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34865         
34866         this.name = hiddenInput.name;
34867         
34868         if(cfg.cn.length > 0) {
34869             cfg.cn.push(hiddenInput);
34870         }
34871         
34872         return cfg;
34873     },
34874
34875     // private
34876     initEvents : function()
34877     {   
34878         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34879         
34880         var allowed = "0123456789";
34881         
34882         if(this.allowDecimals){
34883             allowed += this.decimalSeparator;
34884         }
34885         
34886         if(this.allowNegative){
34887             allowed += "-";
34888         }
34889         
34890         if(this.thousandsDelimiter) {
34891             allowed += ",";
34892         }
34893         
34894         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34895         
34896         var keyPress = function(e){
34897             
34898             var k = e.getKey();
34899             
34900             var c = e.getCharCode();
34901             
34902             if(
34903                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34904                     allowed.indexOf(String.fromCharCode(c)) === -1
34905             ){
34906                 e.stopEvent();
34907                 return;
34908             }
34909             
34910             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34911                 return;
34912             }
34913             
34914             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34915                 e.stopEvent();
34916             }
34917         };
34918         
34919         this.el.on("keypress", keyPress, this);
34920     },
34921     
34922     validateValue : function(value)
34923     {
34924         
34925         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34926             return false;
34927         }
34928         
34929         var num = this.parseValue(value);
34930         
34931         if(isNaN(num)){
34932             this.markInvalid(String.format(this.nanText, value));
34933             return false;
34934         }
34935         
34936         if(num < this.minValue){
34937             this.markInvalid(String.format(this.minText, this.minValue));
34938             return false;
34939         }
34940         
34941         if(num > this.maxValue){
34942             this.markInvalid(String.format(this.maxText, this.maxValue));
34943             return false;
34944         }
34945         
34946         return true;
34947     },
34948
34949     getValue : function()
34950     {
34951         var v = this.hiddenEl().getValue();
34952         
34953         return this.fixPrecision(this.parseValue(v));
34954     },
34955
34956     parseValue : function(value)
34957     {
34958         if(this.thousandsDelimiter) {
34959             value += "";
34960             r = new RegExp(",", "g");
34961             value = value.replace(r, "");
34962         }
34963         
34964         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34965         return isNaN(value) ? '' : value;
34966     },
34967
34968     fixPrecision : function(value)
34969     {
34970         if(this.thousandsDelimiter) {
34971             value += "";
34972             r = new RegExp(",", "g");
34973             value = value.replace(r, "");
34974         }
34975         
34976         var nan = isNaN(value);
34977         
34978         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34979             return nan ? '' : value;
34980         }
34981         return parseFloat(value).toFixed(this.decimalPrecision);
34982     },
34983
34984     setValue : function(v)
34985     {
34986         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34987         
34988         this.value = v;
34989         
34990         if(this.rendered){
34991             
34992             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34993             
34994             this.inputEl().dom.value = (v == '') ? '' :
34995                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34996             
34997             if(!this.allowZero && v === '0') {
34998                 this.hiddenEl().dom.value = '';
34999                 this.inputEl().dom.value = '';
35000             }
35001             
35002             this.validate();
35003         }
35004     },
35005
35006     decimalPrecisionFcn : function(v)
35007     {
35008         return Math.floor(v);
35009     },
35010
35011     beforeBlur : function()
35012     {
35013         var v = this.parseValue(this.getRawValue());
35014         
35015         if(v || v === 0 || v === ''){
35016             this.setValue(v);
35017         }
35018     },
35019     
35020     hiddenEl : function()
35021     {
35022         return this.el.select('input.hidden-number-input',true).first();
35023     }
35024     
35025 });
35026
35027  
35028
35029 /*
35030 * Licence: LGPL
35031 */
35032
35033 /**
35034  * @class Roo.bootstrap.DocumentSlider
35035  * @extends Roo.bootstrap.Component
35036  * Bootstrap DocumentSlider class
35037  * 
35038  * @constructor
35039  * Create a new DocumentViewer
35040  * @param {Object} config The config object
35041  */
35042
35043 Roo.bootstrap.DocumentSlider = function(config){
35044     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35045     
35046     this.files = [];
35047     
35048     this.addEvents({
35049         /**
35050          * @event initial
35051          * Fire after initEvent
35052          * @param {Roo.bootstrap.DocumentSlider} this
35053          */
35054         "initial" : true,
35055         /**
35056          * @event update
35057          * Fire after update
35058          * @param {Roo.bootstrap.DocumentSlider} this
35059          */
35060         "update" : true,
35061         /**
35062          * @event click
35063          * Fire after click
35064          * @param {Roo.bootstrap.DocumentSlider} this
35065          */
35066         "click" : true
35067     });
35068 };
35069
35070 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35071     
35072     files : false,
35073     
35074     indicator : 0,
35075     
35076     getAutoCreate : function()
35077     {
35078         var cfg = {
35079             tag : 'div',
35080             cls : 'roo-document-slider',
35081             cn : [
35082                 {
35083                     tag : 'div',
35084                     cls : 'roo-document-slider-header',
35085                     cn : [
35086                         {
35087                             tag : 'div',
35088                             cls : 'roo-document-slider-header-title'
35089                         }
35090                     ]
35091                 },
35092                 {
35093                     tag : 'div',
35094                     cls : 'roo-document-slider-body',
35095                     cn : [
35096                         {
35097                             tag : 'div',
35098                             cls : 'roo-document-slider-prev',
35099                             cn : [
35100                                 {
35101                                     tag : 'i',
35102                                     cls : 'fa fa-chevron-left'
35103                                 }
35104                             ]
35105                         },
35106                         {
35107                             tag : 'div',
35108                             cls : 'roo-document-slider-thumb',
35109                             cn : [
35110                                 {
35111                                     tag : 'img',
35112                                     cls : 'roo-document-slider-image'
35113                                 }
35114                             ]
35115                         },
35116                         {
35117                             tag : 'div',
35118                             cls : 'roo-document-slider-next',
35119                             cn : [
35120                                 {
35121                                     tag : 'i',
35122                                     cls : 'fa fa-chevron-right'
35123                                 }
35124                             ]
35125                         }
35126                     ]
35127                 }
35128             ]
35129         };
35130         
35131         return cfg;
35132     },
35133     
35134     initEvents : function()
35135     {
35136         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35137         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35138         
35139         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35140         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35141         
35142         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35143         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35144         
35145         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35146         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35147         
35148         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35149         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35150         
35151         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35152         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35153         
35154         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35155         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35156         
35157         this.thumbEl.on('click', this.onClick, this);
35158         
35159         this.prevIndicator.on('click', this.prev, this);
35160         
35161         this.nextIndicator.on('click', this.next, this);
35162         
35163     },
35164     
35165     initial : function()
35166     {
35167         if(this.files.length){
35168             this.indicator = 1;
35169             this.update()
35170         }
35171         
35172         this.fireEvent('initial', this);
35173     },
35174     
35175     update : function()
35176     {
35177         this.imageEl.attr('src', this.files[this.indicator - 1]);
35178         
35179         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35180         
35181         this.prevIndicator.show();
35182         
35183         if(this.indicator == 1){
35184             this.prevIndicator.hide();
35185         }
35186         
35187         this.nextIndicator.show();
35188         
35189         if(this.indicator == this.files.length){
35190             this.nextIndicator.hide();
35191         }
35192         
35193         this.thumbEl.scrollTo('top');
35194         
35195         this.fireEvent('update', this);
35196     },
35197     
35198     onClick : function(e)
35199     {
35200         e.preventDefault();
35201         
35202         this.fireEvent('click', this);
35203     },
35204     
35205     prev : function(e)
35206     {
35207         e.preventDefault();
35208         
35209         this.indicator = Math.max(1, this.indicator - 1);
35210         
35211         this.update();
35212     },
35213     
35214     next : function(e)
35215     {
35216         e.preventDefault();
35217         
35218         this.indicator = Math.min(this.files.length, this.indicator + 1);
35219         
35220         this.update();
35221     }
35222 });
35223 /*
35224  * - LGPL
35225  *
35226  * RadioSet
35227  *
35228  *
35229  */
35230
35231 /**
35232  * @class Roo.bootstrap.RadioSet
35233  * @extends Roo.bootstrap.Input
35234  * Bootstrap RadioSet class
35235  * @cfg {String} indicatorpos (left|right) default left
35236  * @cfg {Boolean} inline (true|false) inline the element (default true)
35237  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35238  * @constructor
35239  * Create a new RadioSet
35240  * @param {Object} config The config object
35241  */
35242
35243 Roo.bootstrap.RadioSet = function(config){
35244     
35245     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35246     
35247     this.radioes = [];
35248     
35249     Roo.bootstrap.RadioSet.register(this);
35250     
35251     this.addEvents({
35252         /**
35253         * @event check
35254         * Fires when the element is checked or unchecked.
35255         * @param {Roo.bootstrap.RadioSet} this This radio
35256         * @param {Roo.bootstrap.Radio} item The checked item
35257         */
35258        check : true,
35259        /**
35260         * @event click
35261         * Fires when the element is click.
35262         * @param {Roo.bootstrap.RadioSet} this This radio set
35263         * @param {Roo.bootstrap.Radio} item The checked item
35264         * @param {Roo.EventObject} e The event object
35265         */
35266        click : true
35267     });
35268     
35269 };
35270
35271 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35272
35273     radioes : false,
35274     
35275     inline : true,
35276     
35277     weight : '',
35278     
35279     indicatorpos : 'left',
35280     
35281     getAutoCreate : function()
35282     {
35283         var label = {
35284             tag : 'label',
35285             cls : 'roo-radio-set-label',
35286             cn : [
35287                 {
35288                     tag : 'span',
35289                     html : this.fieldLabel
35290                 }
35291             ]
35292         };
35293         if (Roo.bootstrap.version == 3) {
35294             
35295             
35296             if(this.indicatorpos == 'left'){
35297                 label.cn.unshift({
35298                     tag : 'i',
35299                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35300                     tooltip : 'This field is required'
35301                 });
35302             } else {
35303                 label.cn.push({
35304                     tag : 'i',
35305                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35306                     tooltip : 'This field is required'
35307                 });
35308             }
35309         }
35310         var items = {
35311             tag : 'div',
35312             cls : 'roo-radio-set-items'
35313         };
35314         
35315         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35316         
35317         if (align === 'left' && this.fieldLabel.length) {
35318             
35319             items = {
35320                 cls : "roo-radio-set-right", 
35321                 cn: [
35322                     items
35323                 ]
35324             };
35325             
35326             if(this.labelWidth > 12){
35327                 label.style = "width: " + this.labelWidth + 'px';
35328             }
35329             
35330             if(this.labelWidth < 13 && this.labelmd == 0){
35331                 this.labelmd = this.labelWidth;
35332             }
35333             
35334             if(this.labellg > 0){
35335                 label.cls += ' col-lg-' + this.labellg;
35336                 items.cls += ' col-lg-' + (12 - this.labellg);
35337             }
35338             
35339             if(this.labelmd > 0){
35340                 label.cls += ' col-md-' + this.labelmd;
35341                 items.cls += ' col-md-' + (12 - this.labelmd);
35342             }
35343             
35344             if(this.labelsm > 0){
35345                 label.cls += ' col-sm-' + this.labelsm;
35346                 items.cls += ' col-sm-' + (12 - this.labelsm);
35347             }
35348             
35349             if(this.labelxs > 0){
35350                 label.cls += ' col-xs-' + this.labelxs;
35351                 items.cls += ' col-xs-' + (12 - this.labelxs);
35352             }
35353         }
35354         
35355         var cfg = {
35356             tag : 'div',
35357             cls : 'roo-radio-set',
35358             cn : [
35359                 {
35360                     tag : 'input',
35361                     cls : 'roo-radio-set-input',
35362                     type : 'hidden',
35363                     name : this.name,
35364                     value : this.value ? this.value :  ''
35365                 },
35366                 label,
35367                 items
35368             ]
35369         };
35370         
35371         if(this.weight.length){
35372             cfg.cls += ' roo-radio-' + this.weight;
35373         }
35374         
35375         if(this.inline) {
35376             cfg.cls += ' roo-radio-set-inline';
35377         }
35378         
35379         var settings=this;
35380         ['xs','sm','md','lg'].map(function(size){
35381             if (settings[size]) {
35382                 cfg.cls += ' col-' + size + '-' + settings[size];
35383             }
35384         });
35385         
35386         return cfg;
35387         
35388     },
35389
35390     initEvents : function()
35391     {
35392         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35393         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35394         
35395         if(!this.fieldLabel.length){
35396             this.labelEl.hide();
35397         }
35398         
35399         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35400         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35401         
35402         this.indicator = this.indicatorEl();
35403         
35404         if(this.indicator){
35405             this.indicator.addClass('invisible');
35406         }
35407         
35408         this.originalValue = this.getValue();
35409         
35410     },
35411     
35412     inputEl: function ()
35413     {
35414         return this.el.select('.roo-radio-set-input', true).first();
35415     },
35416     
35417     getChildContainer : function()
35418     {
35419         return this.itemsEl;
35420     },
35421     
35422     register : function(item)
35423     {
35424         this.radioes.push(item);
35425         
35426     },
35427     
35428     validate : function()
35429     {   
35430         if(this.getVisibilityEl().hasClass('hidden')){
35431             return true;
35432         }
35433         
35434         var valid = false;
35435         
35436         Roo.each(this.radioes, function(i){
35437             if(!i.checked){
35438                 return;
35439             }
35440             
35441             valid = true;
35442             return false;
35443         });
35444         
35445         if(this.allowBlank) {
35446             return true;
35447         }
35448         
35449         if(this.disabled || valid){
35450             this.markValid();
35451             return true;
35452         }
35453         
35454         this.markInvalid();
35455         return false;
35456         
35457     },
35458     
35459     markValid : function()
35460     {
35461         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35462             this.indicatorEl().removeClass('visible');
35463             this.indicatorEl().addClass('invisible');
35464         }
35465         
35466         
35467         if (Roo.bootstrap.version == 3) {
35468             this.el.removeClass([this.invalidClass, this.validClass]);
35469             this.el.addClass(this.validClass);
35470         } else {
35471             this.el.removeClass(['is-invalid','is-valid']);
35472             this.el.addClass(['is-valid']);
35473         }
35474         this.fireEvent('valid', this);
35475     },
35476     
35477     markInvalid : function(msg)
35478     {
35479         if(this.allowBlank || this.disabled){
35480             return;
35481         }
35482         
35483         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35484             this.indicatorEl().removeClass('invisible');
35485             this.indicatorEl().addClass('visible');
35486         }
35487         if (Roo.bootstrap.version == 3) {
35488             this.el.removeClass([this.invalidClass, this.validClass]);
35489             this.el.addClass(this.invalidClass);
35490         } else {
35491             this.el.removeClass(['is-invalid','is-valid']);
35492             this.el.addClass(['is-invalid']);
35493         }
35494         
35495         this.fireEvent('invalid', this, msg);
35496         
35497     },
35498     
35499     setValue : function(v, suppressEvent)
35500     {   
35501         if(this.value === v){
35502             return;
35503         }
35504         
35505         this.value = v;
35506         
35507         if(this.rendered){
35508             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35509         }
35510         
35511         Roo.each(this.radioes, function(i){
35512             i.checked = false;
35513             i.el.removeClass('checked');
35514         });
35515         
35516         Roo.each(this.radioes, function(i){
35517             
35518             if(i.value === v || i.value.toString() === v.toString()){
35519                 i.checked = true;
35520                 i.el.addClass('checked');
35521                 
35522                 if(suppressEvent !== true){
35523                     this.fireEvent('check', this, i);
35524                 }
35525                 
35526                 return false;
35527             }
35528             
35529         }, this);
35530         
35531         this.validate();
35532     },
35533     
35534     clearInvalid : function(){
35535         
35536         if(!this.el || this.preventMark){
35537             return;
35538         }
35539         
35540         this.el.removeClass([this.invalidClass]);
35541         
35542         this.fireEvent('valid', this);
35543     }
35544     
35545 });
35546
35547 Roo.apply(Roo.bootstrap.RadioSet, {
35548     
35549     groups: {},
35550     
35551     register : function(set)
35552     {
35553         this.groups[set.name] = set;
35554     },
35555     
35556     get: function(name) 
35557     {
35558         if (typeof(this.groups[name]) == 'undefined') {
35559             return false;
35560         }
35561         
35562         return this.groups[name] ;
35563     }
35564     
35565 });
35566 /*
35567  * Based on:
35568  * Ext JS Library 1.1.1
35569  * Copyright(c) 2006-2007, Ext JS, LLC.
35570  *
35571  * Originally Released Under LGPL - original licence link has changed is not relivant.
35572  *
35573  * Fork - LGPL
35574  * <script type="text/javascript">
35575  */
35576
35577
35578 /**
35579  * @class Roo.bootstrap.SplitBar
35580  * @extends Roo.util.Observable
35581  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35582  * <br><br>
35583  * Usage:
35584  * <pre><code>
35585 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35586                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35587 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35588 split.minSize = 100;
35589 split.maxSize = 600;
35590 split.animate = true;
35591 split.on('moved', splitterMoved);
35592 </code></pre>
35593  * @constructor
35594  * Create a new SplitBar
35595  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35596  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35597  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35598  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35599                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35600                         position of the SplitBar).
35601  */
35602 Roo.bootstrap.SplitBar = function(cfg){
35603     
35604     /** @private */
35605     
35606     //{
35607     //  dragElement : elm
35608     //  resizingElement: el,
35609         // optional..
35610     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35611     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35612         // existingProxy ???
35613     //}
35614     
35615     this.el = Roo.get(cfg.dragElement, true);
35616     this.el.dom.unselectable = "on";
35617     /** @private */
35618     this.resizingEl = Roo.get(cfg.resizingElement, true);
35619
35620     /**
35621      * @private
35622      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35623      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35624      * @type Number
35625      */
35626     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35627     
35628     /**
35629      * The minimum size of the resizing element. (Defaults to 0)
35630      * @type Number
35631      */
35632     this.minSize = 0;
35633     
35634     /**
35635      * The maximum size of the resizing element. (Defaults to 2000)
35636      * @type Number
35637      */
35638     this.maxSize = 2000;
35639     
35640     /**
35641      * Whether to animate the transition to the new size
35642      * @type Boolean
35643      */
35644     this.animate = false;
35645     
35646     /**
35647      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35648      * @type Boolean
35649      */
35650     this.useShim = false;
35651     
35652     /** @private */
35653     this.shim = null;
35654     
35655     if(!cfg.existingProxy){
35656         /** @private */
35657         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35658     }else{
35659         this.proxy = Roo.get(cfg.existingProxy).dom;
35660     }
35661     /** @private */
35662     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35663     
35664     /** @private */
35665     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35666     
35667     /** @private */
35668     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35669     
35670     /** @private */
35671     this.dragSpecs = {};
35672     
35673     /**
35674      * @private The adapter to use to positon and resize elements
35675      */
35676     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35677     this.adapter.init(this);
35678     
35679     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35680         /** @private */
35681         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35682         this.el.addClass("roo-splitbar-h");
35683     }else{
35684         /** @private */
35685         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35686         this.el.addClass("roo-splitbar-v");
35687     }
35688     
35689     this.addEvents({
35690         /**
35691          * @event resize
35692          * Fires when the splitter is moved (alias for {@link #event-moved})
35693          * @param {Roo.bootstrap.SplitBar} this
35694          * @param {Number} newSize the new width or height
35695          */
35696         "resize" : true,
35697         /**
35698          * @event moved
35699          * Fires when the splitter is moved
35700          * @param {Roo.bootstrap.SplitBar} this
35701          * @param {Number} newSize the new width or height
35702          */
35703         "moved" : true,
35704         /**
35705          * @event beforeresize
35706          * Fires before the splitter is dragged
35707          * @param {Roo.bootstrap.SplitBar} this
35708          */
35709         "beforeresize" : true,
35710
35711         "beforeapply" : true
35712     });
35713
35714     Roo.util.Observable.call(this);
35715 };
35716
35717 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35718     onStartProxyDrag : function(x, y){
35719         this.fireEvent("beforeresize", this);
35720         if(!this.overlay){
35721             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35722             o.unselectable();
35723             o.enableDisplayMode("block");
35724             // all splitbars share the same overlay
35725             Roo.bootstrap.SplitBar.prototype.overlay = o;
35726         }
35727         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35728         this.overlay.show();
35729         Roo.get(this.proxy).setDisplayed("block");
35730         var size = this.adapter.getElementSize(this);
35731         this.activeMinSize = this.getMinimumSize();;
35732         this.activeMaxSize = this.getMaximumSize();;
35733         var c1 = size - this.activeMinSize;
35734         var c2 = Math.max(this.activeMaxSize - size, 0);
35735         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35736             this.dd.resetConstraints();
35737             this.dd.setXConstraint(
35738                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35739                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35740             );
35741             this.dd.setYConstraint(0, 0);
35742         }else{
35743             this.dd.resetConstraints();
35744             this.dd.setXConstraint(0, 0);
35745             this.dd.setYConstraint(
35746                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35747                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35748             );
35749          }
35750         this.dragSpecs.startSize = size;
35751         this.dragSpecs.startPoint = [x, y];
35752         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35753     },
35754     
35755     /** 
35756      * @private Called after the drag operation by the DDProxy
35757      */
35758     onEndProxyDrag : function(e){
35759         Roo.get(this.proxy).setDisplayed(false);
35760         var endPoint = Roo.lib.Event.getXY(e);
35761         if(this.overlay){
35762             this.overlay.hide();
35763         }
35764         var newSize;
35765         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35766             newSize = this.dragSpecs.startSize + 
35767                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35768                     endPoint[0] - this.dragSpecs.startPoint[0] :
35769                     this.dragSpecs.startPoint[0] - endPoint[0]
35770                 );
35771         }else{
35772             newSize = this.dragSpecs.startSize + 
35773                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35774                     endPoint[1] - this.dragSpecs.startPoint[1] :
35775                     this.dragSpecs.startPoint[1] - endPoint[1]
35776                 );
35777         }
35778         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35779         if(newSize != this.dragSpecs.startSize){
35780             if(this.fireEvent('beforeapply', this, newSize) !== false){
35781                 this.adapter.setElementSize(this, newSize);
35782                 this.fireEvent("moved", this, newSize);
35783                 this.fireEvent("resize", this, newSize);
35784             }
35785         }
35786     },
35787     
35788     /**
35789      * Get the adapter this SplitBar uses
35790      * @return The adapter object
35791      */
35792     getAdapter : function(){
35793         return this.adapter;
35794     },
35795     
35796     /**
35797      * Set the adapter this SplitBar uses
35798      * @param {Object} adapter A SplitBar adapter object
35799      */
35800     setAdapter : function(adapter){
35801         this.adapter = adapter;
35802         this.adapter.init(this);
35803     },
35804     
35805     /**
35806      * Gets the minimum size for the resizing element
35807      * @return {Number} The minimum size
35808      */
35809     getMinimumSize : function(){
35810         return this.minSize;
35811     },
35812     
35813     /**
35814      * Sets the minimum size for the resizing element
35815      * @param {Number} minSize The minimum size
35816      */
35817     setMinimumSize : function(minSize){
35818         this.minSize = minSize;
35819     },
35820     
35821     /**
35822      * Gets the maximum size for the resizing element
35823      * @return {Number} The maximum size
35824      */
35825     getMaximumSize : function(){
35826         return this.maxSize;
35827     },
35828     
35829     /**
35830      * Sets the maximum size for the resizing element
35831      * @param {Number} maxSize The maximum size
35832      */
35833     setMaximumSize : function(maxSize){
35834         this.maxSize = maxSize;
35835     },
35836     
35837     /**
35838      * Sets the initialize size for the resizing element
35839      * @param {Number} size The initial size
35840      */
35841     setCurrentSize : function(size){
35842         var oldAnimate = this.animate;
35843         this.animate = false;
35844         this.adapter.setElementSize(this, size);
35845         this.animate = oldAnimate;
35846     },
35847     
35848     /**
35849      * Destroy this splitbar. 
35850      * @param {Boolean} removeEl True to remove the element
35851      */
35852     destroy : function(removeEl){
35853         if(this.shim){
35854             this.shim.remove();
35855         }
35856         this.dd.unreg();
35857         this.proxy.parentNode.removeChild(this.proxy);
35858         if(removeEl){
35859             this.el.remove();
35860         }
35861     }
35862 });
35863
35864 /**
35865  * @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.
35866  */
35867 Roo.bootstrap.SplitBar.createProxy = function(dir){
35868     var proxy = new Roo.Element(document.createElement("div"));
35869     proxy.unselectable();
35870     var cls = 'roo-splitbar-proxy';
35871     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35872     document.body.appendChild(proxy.dom);
35873     return proxy.dom;
35874 };
35875
35876 /** 
35877  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35878  * Default Adapter. It assumes the splitter and resizing element are not positioned
35879  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35880  */
35881 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35882 };
35883
35884 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35885     // do nothing for now
35886     init : function(s){
35887     
35888     },
35889     /**
35890      * Called before drag operations to get the current size of the resizing element. 
35891      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35892      */
35893      getElementSize : function(s){
35894         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35895             return s.resizingEl.getWidth();
35896         }else{
35897             return s.resizingEl.getHeight();
35898         }
35899     },
35900     
35901     /**
35902      * Called after drag operations to set the size of the resizing element.
35903      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35904      * @param {Number} newSize The new size to set
35905      * @param {Function} onComplete A function to be invoked when resizing is complete
35906      */
35907     setElementSize : function(s, newSize, onComplete){
35908         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35909             if(!s.animate){
35910                 s.resizingEl.setWidth(newSize);
35911                 if(onComplete){
35912                     onComplete(s, newSize);
35913                 }
35914             }else{
35915                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35916             }
35917         }else{
35918             
35919             if(!s.animate){
35920                 s.resizingEl.setHeight(newSize);
35921                 if(onComplete){
35922                     onComplete(s, newSize);
35923                 }
35924             }else{
35925                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35926             }
35927         }
35928     }
35929 };
35930
35931 /** 
35932  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35933  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35934  * Adapter that  moves the splitter element to align with the resized sizing element. 
35935  * Used with an absolute positioned SplitBar.
35936  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35937  * document.body, make sure you assign an id to the body element.
35938  */
35939 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35940     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35941     this.container = Roo.get(container);
35942 };
35943
35944 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35945     init : function(s){
35946         this.basic.init(s);
35947     },
35948     
35949     getElementSize : function(s){
35950         return this.basic.getElementSize(s);
35951     },
35952     
35953     setElementSize : function(s, newSize, onComplete){
35954         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35955     },
35956     
35957     moveSplitter : function(s){
35958         var yes = Roo.bootstrap.SplitBar;
35959         switch(s.placement){
35960             case yes.LEFT:
35961                 s.el.setX(s.resizingEl.getRight());
35962                 break;
35963             case yes.RIGHT:
35964                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35965                 break;
35966             case yes.TOP:
35967                 s.el.setY(s.resizingEl.getBottom());
35968                 break;
35969             case yes.BOTTOM:
35970                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35971                 break;
35972         }
35973     }
35974 };
35975
35976 /**
35977  * Orientation constant - Create a vertical SplitBar
35978  * @static
35979  * @type Number
35980  */
35981 Roo.bootstrap.SplitBar.VERTICAL = 1;
35982
35983 /**
35984  * Orientation constant - Create a horizontal SplitBar
35985  * @static
35986  * @type Number
35987  */
35988 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35989
35990 /**
35991  * Placement constant - The resizing element is to the left of the splitter element
35992  * @static
35993  * @type Number
35994  */
35995 Roo.bootstrap.SplitBar.LEFT = 1;
35996
35997 /**
35998  * Placement constant - The resizing element is to the right of the splitter element
35999  * @static
36000  * @type Number
36001  */
36002 Roo.bootstrap.SplitBar.RIGHT = 2;
36003
36004 /**
36005  * Placement constant - The resizing element is positioned above the splitter element
36006  * @static
36007  * @type Number
36008  */
36009 Roo.bootstrap.SplitBar.TOP = 3;
36010
36011 /**
36012  * Placement constant - The resizing element is positioned under splitter element
36013  * @static
36014  * @type Number
36015  */
36016 Roo.bootstrap.SplitBar.BOTTOM = 4;
36017 Roo.namespace("Roo.bootstrap.layout");/*
36018  * Based on:
36019  * Ext JS Library 1.1.1
36020  * Copyright(c) 2006-2007, Ext JS, LLC.
36021  *
36022  * Originally Released Under LGPL - original licence link has changed is not relivant.
36023  *
36024  * Fork - LGPL
36025  * <script type="text/javascript">
36026  */
36027
36028 /**
36029  * @class Roo.bootstrap.layout.Manager
36030  * @extends Roo.bootstrap.Component
36031  * Base class for layout managers.
36032  */
36033 Roo.bootstrap.layout.Manager = function(config)
36034 {
36035     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36036
36037
36038
36039
36040
36041     /** false to disable window resize monitoring @type Boolean */
36042     this.monitorWindowResize = true;
36043     this.regions = {};
36044     this.addEvents({
36045         /**
36046          * @event layout
36047          * Fires when a layout is performed.
36048          * @param {Roo.LayoutManager} this
36049          */
36050         "layout" : true,
36051         /**
36052          * @event regionresized
36053          * Fires when the user resizes a region.
36054          * @param {Roo.LayoutRegion} region The resized region
36055          * @param {Number} newSize The new size (width for east/west, height for north/south)
36056          */
36057         "regionresized" : true,
36058         /**
36059          * @event regioncollapsed
36060          * Fires when a region is collapsed.
36061          * @param {Roo.LayoutRegion} region The collapsed region
36062          */
36063         "regioncollapsed" : true,
36064         /**
36065          * @event regionexpanded
36066          * Fires when a region is expanded.
36067          * @param {Roo.LayoutRegion} region The expanded region
36068          */
36069         "regionexpanded" : true
36070     });
36071     this.updating = false;
36072
36073     if (config.el) {
36074         this.el = Roo.get(config.el);
36075         this.initEvents();
36076     }
36077
36078 };
36079
36080 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36081
36082
36083     regions : null,
36084
36085     monitorWindowResize : true,
36086
36087
36088     updating : false,
36089
36090
36091     onRender : function(ct, position)
36092     {
36093         if(!this.el){
36094             this.el = Roo.get(ct);
36095             this.initEvents();
36096         }
36097         //this.fireEvent('render',this);
36098     },
36099
36100
36101     initEvents: function()
36102     {
36103
36104
36105         // ie scrollbar fix
36106         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36107             document.body.scroll = "no";
36108         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36109             this.el.position('relative');
36110         }
36111         this.id = this.el.id;
36112         this.el.addClass("roo-layout-container");
36113         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36114         if(this.el.dom != document.body ) {
36115             this.el.on('resize', this.layout,this);
36116             this.el.on('show', this.layout,this);
36117         }
36118
36119     },
36120
36121     /**
36122      * Returns true if this layout is currently being updated
36123      * @return {Boolean}
36124      */
36125     isUpdating : function(){
36126         return this.updating;
36127     },
36128
36129     /**
36130      * Suspend the LayoutManager from doing auto-layouts while
36131      * making multiple add or remove calls
36132      */
36133     beginUpdate : function(){
36134         this.updating = true;
36135     },
36136
36137     /**
36138      * Restore auto-layouts and optionally disable the manager from performing a layout
36139      * @param {Boolean} noLayout true to disable a layout update
36140      */
36141     endUpdate : function(noLayout){
36142         this.updating = false;
36143         if(!noLayout){
36144             this.layout();
36145         }
36146     },
36147
36148     layout: function(){
36149         // abstract...
36150     },
36151
36152     onRegionResized : function(region, newSize){
36153         this.fireEvent("regionresized", region, newSize);
36154         this.layout();
36155     },
36156
36157     onRegionCollapsed : function(region){
36158         this.fireEvent("regioncollapsed", region);
36159     },
36160
36161     onRegionExpanded : function(region){
36162         this.fireEvent("regionexpanded", region);
36163     },
36164
36165     /**
36166      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36167      * performs box-model adjustments.
36168      * @return {Object} The size as an object {width: (the width), height: (the height)}
36169      */
36170     getViewSize : function()
36171     {
36172         var size;
36173         if(this.el.dom != document.body){
36174             size = this.el.getSize();
36175         }else{
36176             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36177         }
36178         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36179         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36180         return size;
36181     },
36182
36183     /**
36184      * Returns the Element this layout is bound to.
36185      * @return {Roo.Element}
36186      */
36187     getEl : function(){
36188         return this.el;
36189     },
36190
36191     /**
36192      * Returns the specified region.
36193      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36194      * @return {Roo.LayoutRegion}
36195      */
36196     getRegion : function(target){
36197         return this.regions[target.toLowerCase()];
36198     },
36199
36200     onWindowResize : function(){
36201         if(this.monitorWindowResize){
36202             this.layout();
36203         }
36204     }
36205 });
36206 /*
36207  * Based on:
36208  * Ext JS Library 1.1.1
36209  * Copyright(c) 2006-2007, Ext JS, LLC.
36210  *
36211  * Originally Released Under LGPL - original licence link has changed is not relivant.
36212  *
36213  * Fork - LGPL
36214  * <script type="text/javascript">
36215  */
36216 /**
36217  * @class Roo.bootstrap.layout.Border
36218  * @extends Roo.bootstrap.layout.Manager
36219  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36220  * please see: examples/bootstrap/nested.html<br><br>
36221  
36222 <b>The container the layout is rendered into can be either the body element or any other element.
36223 If it is not the body element, the container needs to either be an absolute positioned element,
36224 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36225 the container size if it is not the body element.</b>
36226
36227 * @constructor
36228 * Create a new Border
36229 * @param {Object} config Configuration options
36230  */
36231 Roo.bootstrap.layout.Border = function(config){
36232     config = config || {};
36233     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36234     
36235     
36236     
36237     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36238         if(config[region]){
36239             config[region].region = region;
36240             this.addRegion(config[region]);
36241         }
36242     },this);
36243     
36244 };
36245
36246 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36247
36248 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36249     
36250     parent : false, // this might point to a 'nest' or a ???
36251     
36252     /**
36253      * Creates and adds a new region if it doesn't already exist.
36254      * @param {String} target The target region key (north, south, east, west or center).
36255      * @param {Object} config The regions config object
36256      * @return {BorderLayoutRegion} The new region
36257      */
36258     addRegion : function(config)
36259     {
36260         if(!this.regions[config.region]){
36261             var r = this.factory(config);
36262             this.bindRegion(r);
36263         }
36264         return this.regions[config.region];
36265     },
36266
36267     // private (kinda)
36268     bindRegion : function(r){
36269         this.regions[r.config.region] = r;
36270         
36271         r.on("visibilitychange",    this.layout, this);
36272         r.on("paneladded",          this.layout, this);
36273         r.on("panelremoved",        this.layout, this);
36274         r.on("invalidated",         this.layout, this);
36275         r.on("resized",             this.onRegionResized, this);
36276         r.on("collapsed",           this.onRegionCollapsed, this);
36277         r.on("expanded",            this.onRegionExpanded, this);
36278     },
36279
36280     /**
36281      * Performs a layout update.
36282      */
36283     layout : function()
36284     {
36285         if(this.updating) {
36286             return;
36287         }
36288         
36289         // render all the rebions if they have not been done alreayd?
36290         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36291             if(this.regions[region] && !this.regions[region].bodyEl){
36292                 this.regions[region].onRender(this.el)
36293             }
36294         },this);
36295         
36296         var size = this.getViewSize();
36297         var w = size.width;
36298         var h = size.height;
36299         var centerW = w;
36300         var centerH = h;
36301         var centerY = 0;
36302         var centerX = 0;
36303         //var x = 0, y = 0;
36304
36305         var rs = this.regions;
36306         var north = rs["north"];
36307         var south = rs["south"]; 
36308         var west = rs["west"];
36309         var east = rs["east"];
36310         var center = rs["center"];
36311         //if(this.hideOnLayout){ // not supported anymore
36312             //c.el.setStyle("display", "none");
36313         //}
36314         if(north && north.isVisible()){
36315             var b = north.getBox();
36316             var m = north.getMargins();
36317             b.width = w - (m.left+m.right);
36318             b.x = m.left;
36319             b.y = m.top;
36320             centerY = b.height + b.y + m.bottom;
36321             centerH -= centerY;
36322             north.updateBox(this.safeBox(b));
36323         }
36324         if(south && south.isVisible()){
36325             var b = south.getBox();
36326             var m = south.getMargins();
36327             b.width = w - (m.left+m.right);
36328             b.x = m.left;
36329             var totalHeight = (b.height + m.top + m.bottom);
36330             b.y = h - totalHeight + m.top;
36331             centerH -= totalHeight;
36332             south.updateBox(this.safeBox(b));
36333         }
36334         if(west && west.isVisible()){
36335             var b = west.getBox();
36336             var m = west.getMargins();
36337             b.height = centerH - (m.top+m.bottom);
36338             b.x = m.left;
36339             b.y = centerY + m.top;
36340             var totalWidth = (b.width + m.left + m.right);
36341             centerX += totalWidth;
36342             centerW -= totalWidth;
36343             west.updateBox(this.safeBox(b));
36344         }
36345         if(east && east.isVisible()){
36346             var b = east.getBox();
36347             var m = east.getMargins();
36348             b.height = centerH - (m.top+m.bottom);
36349             var totalWidth = (b.width + m.left + m.right);
36350             b.x = w - totalWidth + m.left;
36351             b.y = centerY + m.top;
36352             centerW -= totalWidth;
36353             east.updateBox(this.safeBox(b));
36354         }
36355         if(center){
36356             var m = center.getMargins();
36357             var centerBox = {
36358                 x: centerX + m.left,
36359                 y: centerY + m.top,
36360                 width: centerW - (m.left+m.right),
36361                 height: centerH - (m.top+m.bottom)
36362             };
36363             //if(this.hideOnLayout){
36364                 //center.el.setStyle("display", "block");
36365             //}
36366             center.updateBox(this.safeBox(centerBox));
36367         }
36368         this.el.repaint();
36369         this.fireEvent("layout", this);
36370     },
36371
36372     // private
36373     safeBox : function(box){
36374         box.width = Math.max(0, box.width);
36375         box.height = Math.max(0, box.height);
36376         return box;
36377     },
36378
36379     /**
36380      * Adds a ContentPanel (or subclass) to this layout.
36381      * @param {String} target The target region key (north, south, east, west or center).
36382      * @param {Roo.ContentPanel} panel The panel to add
36383      * @return {Roo.ContentPanel} The added panel
36384      */
36385     add : function(target, panel){
36386          
36387         target = target.toLowerCase();
36388         return this.regions[target].add(panel);
36389     },
36390
36391     /**
36392      * Remove a ContentPanel (or subclass) to this layout.
36393      * @param {String} target The target region key (north, south, east, west or center).
36394      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36395      * @return {Roo.ContentPanel} The removed panel
36396      */
36397     remove : function(target, panel){
36398         target = target.toLowerCase();
36399         return this.regions[target].remove(panel);
36400     },
36401
36402     /**
36403      * Searches all regions for a panel with the specified id
36404      * @param {String} panelId
36405      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36406      */
36407     findPanel : function(panelId){
36408         var rs = this.regions;
36409         for(var target in rs){
36410             if(typeof rs[target] != "function"){
36411                 var p = rs[target].getPanel(panelId);
36412                 if(p){
36413                     return p;
36414                 }
36415             }
36416         }
36417         return null;
36418     },
36419
36420     /**
36421      * Searches all regions for a panel with the specified id and activates (shows) it.
36422      * @param {String/ContentPanel} panelId The panels id or the panel itself
36423      * @return {Roo.ContentPanel} The shown panel or null
36424      */
36425     showPanel : function(panelId) {
36426       var rs = this.regions;
36427       for(var target in rs){
36428          var r = rs[target];
36429          if(typeof r != "function"){
36430             if(r.hasPanel(panelId)){
36431                return r.showPanel(panelId);
36432             }
36433          }
36434       }
36435       return null;
36436    },
36437
36438    /**
36439      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36440      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36441      */
36442    /*
36443     restoreState : function(provider){
36444         if(!provider){
36445             provider = Roo.state.Manager;
36446         }
36447         var sm = new Roo.LayoutStateManager();
36448         sm.init(this, provider);
36449     },
36450 */
36451  
36452  
36453     /**
36454      * Adds a xtype elements to the layout.
36455      * <pre><code>
36456
36457 layout.addxtype({
36458        xtype : 'ContentPanel',
36459        region: 'west',
36460        items: [ .... ]
36461    }
36462 );
36463
36464 layout.addxtype({
36465         xtype : 'NestedLayoutPanel',
36466         region: 'west',
36467         layout: {
36468            center: { },
36469            west: { }   
36470         },
36471         items : [ ... list of content panels or nested layout panels.. ]
36472    }
36473 );
36474 </code></pre>
36475      * @param {Object} cfg Xtype definition of item to add.
36476      */
36477     addxtype : function(cfg)
36478     {
36479         // basically accepts a pannel...
36480         // can accept a layout region..!?!?
36481         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36482         
36483         
36484         // theory?  children can only be panels??
36485         
36486         //if (!cfg.xtype.match(/Panel$/)) {
36487         //    return false;
36488         //}
36489         var ret = false;
36490         
36491         if (typeof(cfg.region) == 'undefined') {
36492             Roo.log("Failed to add Panel, region was not set");
36493             Roo.log(cfg);
36494             return false;
36495         }
36496         var region = cfg.region;
36497         delete cfg.region;
36498         
36499           
36500         var xitems = [];
36501         if (cfg.items) {
36502             xitems = cfg.items;
36503             delete cfg.items;
36504         }
36505         var nb = false;
36506         
36507         if ( region == 'center') {
36508             Roo.log("Center: " + cfg.title);
36509         }
36510         
36511         
36512         switch(cfg.xtype) 
36513         {
36514             case 'Content':  // ContentPanel (el, cfg)
36515             case 'Scroll':  // ContentPanel (el, cfg)
36516             case 'View': 
36517                 cfg.autoCreate = cfg.autoCreate || true;
36518                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36519                 //} else {
36520                 //    var el = this.el.createChild();
36521                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36522                 //}
36523                 
36524                 this.add(region, ret);
36525                 break;
36526             
36527             /*
36528             case 'TreePanel': // our new panel!
36529                 cfg.el = this.el.createChild();
36530                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36531                 this.add(region, ret);
36532                 break;
36533             */
36534             
36535             case 'Nest': 
36536                 // create a new Layout (which is  a Border Layout...
36537                 
36538                 var clayout = cfg.layout;
36539                 clayout.el  = this.el.createChild();
36540                 clayout.items   = clayout.items  || [];
36541                 
36542                 delete cfg.layout;
36543                 
36544                 // replace this exitems with the clayout ones..
36545                 xitems = clayout.items;
36546                  
36547                 // force background off if it's in center...
36548                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36549                     cfg.background = false;
36550                 }
36551                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36552                 
36553                 
36554                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36555                 //console.log('adding nested layout panel '  + cfg.toSource());
36556                 this.add(region, ret);
36557                 nb = {}; /// find first...
36558                 break;
36559             
36560             case 'Grid':
36561                 
36562                 // needs grid and region
36563                 
36564                 //var el = this.getRegion(region).el.createChild();
36565                 /*
36566                  *var el = this.el.createChild();
36567                 // create the grid first...
36568                 cfg.grid.container = el;
36569                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36570                 */
36571                 
36572                 if (region == 'center' && this.active ) {
36573                     cfg.background = false;
36574                 }
36575                 
36576                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36577                 
36578                 this.add(region, ret);
36579                 /*
36580                 if (cfg.background) {
36581                     // render grid on panel activation (if panel background)
36582                     ret.on('activate', function(gp) {
36583                         if (!gp.grid.rendered) {
36584                     //        gp.grid.render(el);
36585                         }
36586                     });
36587                 } else {
36588                   //  cfg.grid.render(el);
36589                 }
36590                 */
36591                 break;
36592            
36593            
36594             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36595                 // it was the old xcomponent building that caused this before.
36596                 // espeically if border is the top element in the tree.
36597                 ret = this;
36598                 break; 
36599                 
36600                     
36601                 
36602                 
36603                 
36604             default:
36605                 /*
36606                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36607                     
36608                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36609                     this.add(region, ret);
36610                 } else {
36611                 */
36612                     Roo.log(cfg);
36613                     throw "Can not add '" + cfg.xtype + "' to Border";
36614                     return null;
36615              
36616                                 
36617              
36618         }
36619         this.beginUpdate();
36620         // add children..
36621         var region = '';
36622         var abn = {};
36623         Roo.each(xitems, function(i)  {
36624             region = nb && i.region ? i.region : false;
36625             
36626             var add = ret.addxtype(i);
36627            
36628             if (region) {
36629                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36630                 if (!i.background) {
36631                     abn[region] = nb[region] ;
36632                 }
36633             }
36634             
36635         });
36636         this.endUpdate();
36637
36638         // make the last non-background panel active..
36639         //if (nb) { Roo.log(abn); }
36640         if (nb) {
36641             
36642             for(var r in abn) {
36643                 region = this.getRegion(r);
36644                 if (region) {
36645                     // tried using nb[r], but it does not work..
36646                      
36647                     region.showPanel(abn[r]);
36648                    
36649                 }
36650             }
36651         }
36652         return ret;
36653         
36654     },
36655     
36656     
36657 // private
36658     factory : function(cfg)
36659     {
36660         
36661         var validRegions = Roo.bootstrap.layout.Border.regions;
36662
36663         var target = cfg.region;
36664         cfg.mgr = this;
36665         
36666         var r = Roo.bootstrap.layout;
36667         Roo.log(target);
36668         switch(target){
36669             case "north":
36670                 return new r.North(cfg);
36671             case "south":
36672                 return new r.South(cfg);
36673             case "east":
36674                 return new r.East(cfg);
36675             case "west":
36676                 return new r.West(cfg);
36677             case "center":
36678                 return new r.Center(cfg);
36679         }
36680         throw 'Layout region "'+target+'" not supported.';
36681     }
36682     
36683     
36684 });
36685  /*
36686  * Based on:
36687  * Ext JS Library 1.1.1
36688  * Copyright(c) 2006-2007, Ext JS, LLC.
36689  *
36690  * Originally Released Under LGPL - original licence link has changed is not relivant.
36691  *
36692  * Fork - LGPL
36693  * <script type="text/javascript">
36694  */
36695  
36696 /**
36697  * @class Roo.bootstrap.layout.Basic
36698  * @extends Roo.util.Observable
36699  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36700  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36701  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36702  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36703  * @cfg {string}   region  the region that it inhabits..
36704  * @cfg {bool}   skipConfig skip config?
36705  * 
36706
36707  */
36708 Roo.bootstrap.layout.Basic = function(config){
36709     
36710     this.mgr = config.mgr;
36711     
36712     this.position = config.region;
36713     
36714     var skipConfig = config.skipConfig;
36715     
36716     this.events = {
36717         /**
36718          * @scope Roo.BasicLayoutRegion
36719          */
36720         
36721         /**
36722          * @event beforeremove
36723          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36724          * @param {Roo.LayoutRegion} this
36725          * @param {Roo.ContentPanel} panel The panel
36726          * @param {Object} e The cancel event object
36727          */
36728         "beforeremove" : true,
36729         /**
36730          * @event invalidated
36731          * Fires when the layout for this region is changed.
36732          * @param {Roo.LayoutRegion} this
36733          */
36734         "invalidated" : true,
36735         /**
36736          * @event visibilitychange
36737          * Fires when this region is shown or hidden 
36738          * @param {Roo.LayoutRegion} this
36739          * @param {Boolean} visibility true or false
36740          */
36741         "visibilitychange" : true,
36742         /**
36743          * @event paneladded
36744          * Fires when a panel is added. 
36745          * @param {Roo.LayoutRegion} this
36746          * @param {Roo.ContentPanel} panel The panel
36747          */
36748         "paneladded" : true,
36749         /**
36750          * @event panelremoved
36751          * Fires when a panel is removed. 
36752          * @param {Roo.LayoutRegion} this
36753          * @param {Roo.ContentPanel} panel The panel
36754          */
36755         "panelremoved" : true,
36756         /**
36757          * @event beforecollapse
36758          * Fires when this region before collapse.
36759          * @param {Roo.LayoutRegion} this
36760          */
36761         "beforecollapse" : true,
36762         /**
36763          * @event collapsed
36764          * Fires when this region is collapsed.
36765          * @param {Roo.LayoutRegion} this
36766          */
36767         "collapsed" : true,
36768         /**
36769          * @event expanded
36770          * Fires when this region is expanded.
36771          * @param {Roo.LayoutRegion} this
36772          */
36773         "expanded" : true,
36774         /**
36775          * @event slideshow
36776          * Fires when this region is slid into view.
36777          * @param {Roo.LayoutRegion} this
36778          */
36779         "slideshow" : true,
36780         /**
36781          * @event slidehide
36782          * Fires when this region slides out of view. 
36783          * @param {Roo.LayoutRegion} this
36784          */
36785         "slidehide" : true,
36786         /**
36787          * @event panelactivated
36788          * Fires when a panel is activated. 
36789          * @param {Roo.LayoutRegion} this
36790          * @param {Roo.ContentPanel} panel The activated panel
36791          */
36792         "panelactivated" : true,
36793         /**
36794          * @event resized
36795          * Fires when the user resizes this region. 
36796          * @param {Roo.LayoutRegion} this
36797          * @param {Number} newSize The new size (width for east/west, height for north/south)
36798          */
36799         "resized" : true
36800     };
36801     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36802     this.panels = new Roo.util.MixedCollection();
36803     this.panels.getKey = this.getPanelId.createDelegate(this);
36804     this.box = null;
36805     this.activePanel = null;
36806     // ensure listeners are added...
36807     
36808     if (config.listeners || config.events) {
36809         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36810             listeners : config.listeners || {},
36811             events : config.events || {}
36812         });
36813     }
36814     
36815     if(skipConfig !== true){
36816         this.applyConfig(config);
36817     }
36818 };
36819
36820 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36821 {
36822     getPanelId : function(p){
36823         return p.getId();
36824     },
36825     
36826     applyConfig : function(config){
36827         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36828         this.config = config;
36829         
36830     },
36831     
36832     /**
36833      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36834      * the width, for horizontal (north, south) the height.
36835      * @param {Number} newSize The new width or height
36836      */
36837     resizeTo : function(newSize){
36838         var el = this.el ? this.el :
36839                  (this.activePanel ? this.activePanel.getEl() : null);
36840         if(el){
36841             switch(this.position){
36842                 case "east":
36843                 case "west":
36844                     el.setWidth(newSize);
36845                     this.fireEvent("resized", this, newSize);
36846                 break;
36847                 case "north":
36848                 case "south":
36849                     el.setHeight(newSize);
36850                     this.fireEvent("resized", this, newSize);
36851                 break;                
36852             }
36853         }
36854     },
36855     
36856     getBox : function(){
36857         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36858     },
36859     
36860     getMargins : function(){
36861         return this.margins;
36862     },
36863     
36864     updateBox : function(box){
36865         this.box = box;
36866         var el = this.activePanel.getEl();
36867         el.dom.style.left = box.x + "px";
36868         el.dom.style.top = box.y + "px";
36869         this.activePanel.setSize(box.width, box.height);
36870     },
36871     
36872     /**
36873      * Returns the container element for this region.
36874      * @return {Roo.Element}
36875      */
36876     getEl : function(){
36877         return this.activePanel;
36878     },
36879     
36880     /**
36881      * Returns true if this region is currently visible.
36882      * @return {Boolean}
36883      */
36884     isVisible : function(){
36885         return this.activePanel ? true : false;
36886     },
36887     
36888     setActivePanel : function(panel){
36889         panel = this.getPanel(panel);
36890         if(this.activePanel && this.activePanel != panel){
36891             this.activePanel.setActiveState(false);
36892             this.activePanel.getEl().setLeftTop(-10000,-10000);
36893         }
36894         this.activePanel = panel;
36895         panel.setActiveState(true);
36896         if(this.box){
36897             panel.setSize(this.box.width, this.box.height);
36898         }
36899         this.fireEvent("panelactivated", this, panel);
36900         this.fireEvent("invalidated");
36901     },
36902     
36903     /**
36904      * Show the specified panel.
36905      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36906      * @return {Roo.ContentPanel} The shown panel or null
36907      */
36908     showPanel : function(panel){
36909         panel = this.getPanel(panel);
36910         if(panel){
36911             this.setActivePanel(panel);
36912         }
36913         return panel;
36914     },
36915     
36916     /**
36917      * Get the active panel for this region.
36918      * @return {Roo.ContentPanel} The active panel or null
36919      */
36920     getActivePanel : function(){
36921         return this.activePanel;
36922     },
36923     
36924     /**
36925      * Add the passed ContentPanel(s)
36926      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36927      * @return {Roo.ContentPanel} The panel added (if only one was added)
36928      */
36929     add : function(panel){
36930         if(arguments.length > 1){
36931             for(var i = 0, len = arguments.length; i < len; i++) {
36932                 this.add(arguments[i]);
36933             }
36934             return null;
36935         }
36936         if(this.hasPanel(panel)){
36937             this.showPanel(panel);
36938             return panel;
36939         }
36940         var el = panel.getEl();
36941         if(el.dom.parentNode != this.mgr.el.dom){
36942             this.mgr.el.dom.appendChild(el.dom);
36943         }
36944         if(panel.setRegion){
36945             panel.setRegion(this);
36946         }
36947         this.panels.add(panel);
36948         el.setStyle("position", "absolute");
36949         if(!panel.background){
36950             this.setActivePanel(panel);
36951             if(this.config.initialSize && this.panels.getCount()==1){
36952                 this.resizeTo(this.config.initialSize);
36953             }
36954         }
36955         this.fireEvent("paneladded", this, panel);
36956         return panel;
36957     },
36958     
36959     /**
36960      * Returns true if the panel is in this region.
36961      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36962      * @return {Boolean}
36963      */
36964     hasPanel : function(panel){
36965         if(typeof panel == "object"){ // must be panel obj
36966             panel = panel.getId();
36967         }
36968         return this.getPanel(panel) ? true : false;
36969     },
36970     
36971     /**
36972      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36973      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36974      * @param {Boolean} preservePanel Overrides the config preservePanel option
36975      * @return {Roo.ContentPanel} The panel that was removed
36976      */
36977     remove : function(panel, preservePanel){
36978         panel = this.getPanel(panel);
36979         if(!panel){
36980             return null;
36981         }
36982         var e = {};
36983         this.fireEvent("beforeremove", this, panel, e);
36984         if(e.cancel === true){
36985             return null;
36986         }
36987         var panelId = panel.getId();
36988         this.panels.removeKey(panelId);
36989         return panel;
36990     },
36991     
36992     /**
36993      * Returns the panel specified or null if it's not in this region.
36994      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36995      * @return {Roo.ContentPanel}
36996      */
36997     getPanel : function(id){
36998         if(typeof id == "object"){ // must be panel obj
36999             return id;
37000         }
37001         return this.panels.get(id);
37002     },
37003     
37004     /**
37005      * Returns this regions position (north/south/east/west/center).
37006      * @return {String} 
37007      */
37008     getPosition: function(){
37009         return this.position;    
37010     }
37011 });/*
37012  * Based on:
37013  * Ext JS Library 1.1.1
37014  * Copyright(c) 2006-2007, Ext JS, LLC.
37015  *
37016  * Originally Released Under LGPL - original licence link has changed is not relivant.
37017  *
37018  * Fork - LGPL
37019  * <script type="text/javascript">
37020  */
37021  
37022 /**
37023  * @class Roo.bootstrap.layout.Region
37024  * @extends Roo.bootstrap.layout.Basic
37025  * This class represents a region in a layout manager.
37026  
37027  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37028  * @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})
37029  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37030  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37031  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37032  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37033  * @cfg {String}    title           The title for the region (overrides panel titles)
37034  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37035  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37036  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37037  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37038  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37039  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37040  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37041  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37042  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37043  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37044
37045  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37046  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37047  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37048  * @cfg {Number}    width           For East/West panels
37049  * @cfg {Number}    height          For North/South panels
37050  * @cfg {Boolean}   split           To show the splitter
37051  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37052  * 
37053  * @cfg {string}   cls             Extra CSS classes to add to region
37054  * 
37055  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37056  * @cfg {string}   region  the region that it inhabits..
37057  *
37058
37059  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37060  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37061
37062  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37063  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37064  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37065  */
37066 Roo.bootstrap.layout.Region = function(config)
37067 {
37068     this.applyConfig(config);
37069
37070     var mgr = config.mgr;
37071     var pos = config.region;
37072     config.skipConfig = true;
37073     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37074     
37075     if (mgr.el) {
37076         this.onRender(mgr.el);   
37077     }
37078      
37079     this.visible = true;
37080     this.collapsed = false;
37081     this.unrendered_panels = [];
37082 };
37083
37084 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37085
37086     position: '', // set by wrapper (eg. north/south etc..)
37087     unrendered_panels : null,  // unrendered panels.
37088     
37089     tabPosition : false,
37090     
37091     mgr: false, // points to 'Border'
37092     
37093     
37094     createBody : function(){
37095         /** This region's body element 
37096         * @type Roo.Element */
37097         this.bodyEl = this.el.createChild({
37098                 tag: "div",
37099                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37100         });
37101     },
37102
37103     onRender: function(ctr, pos)
37104     {
37105         var dh = Roo.DomHelper;
37106         /** This region's container element 
37107         * @type Roo.Element */
37108         this.el = dh.append(ctr.dom, {
37109                 tag: "div",
37110                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37111             }, true);
37112         /** This region's title element 
37113         * @type Roo.Element */
37114     
37115         this.titleEl = dh.append(this.el.dom,  {
37116                 tag: "div",
37117                 unselectable: "on",
37118                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37119                 children:[
37120                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37121                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37122                 ]
37123             }, true);
37124         
37125         this.titleEl.enableDisplayMode();
37126         /** This region's title text element 
37127         * @type HTMLElement */
37128         this.titleTextEl = this.titleEl.dom.firstChild;
37129         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37130         /*
37131         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37132         this.closeBtn.enableDisplayMode();
37133         this.closeBtn.on("click", this.closeClicked, this);
37134         this.closeBtn.hide();
37135     */
37136         this.createBody(this.config);
37137         if(this.config.hideWhenEmpty){
37138             this.hide();
37139             this.on("paneladded", this.validateVisibility, this);
37140             this.on("panelremoved", this.validateVisibility, this);
37141         }
37142         if(this.autoScroll){
37143             this.bodyEl.setStyle("overflow", "auto");
37144         }else{
37145             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37146         }
37147         //if(c.titlebar !== false){
37148             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37149                 this.titleEl.hide();
37150             }else{
37151                 this.titleEl.show();
37152                 if(this.config.title){
37153                     this.titleTextEl.innerHTML = this.config.title;
37154                 }
37155             }
37156         //}
37157         if(this.config.collapsed){
37158             this.collapse(true);
37159         }
37160         if(this.config.hidden){
37161             this.hide();
37162         }
37163         
37164         if (this.unrendered_panels && this.unrendered_panels.length) {
37165             for (var i =0;i< this.unrendered_panels.length; i++) {
37166                 this.add(this.unrendered_panels[i]);
37167             }
37168             this.unrendered_panels = null;
37169             
37170         }
37171         
37172     },
37173     
37174     applyConfig : function(c)
37175     {
37176         /*
37177          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37178             var dh = Roo.DomHelper;
37179             if(c.titlebar !== false){
37180                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37181                 this.collapseBtn.on("click", this.collapse, this);
37182                 this.collapseBtn.enableDisplayMode();
37183                 /*
37184                 if(c.showPin === true || this.showPin){
37185                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37186                     this.stickBtn.enableDisplayMode();
37187                     this.stickBtn.on("click", this.expand, this);
37188                     this.stickBtn.hide();
37189                 }
37190                 
37191             }
37192             */
37193             /** This region's collapsed element
37194             * @type Roo.Element */
37195             /*
37196              *
37197             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37198                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37199             ]}, true);
37200             
37201             if(c.floatable !== false){
37202                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37203                this.collapsedEl.on("click", this.collapseClick, this);
37204             }
37205
37206             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37207                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37208                    id: "message", unselectable: "on", style:{"float":"left"}});
37209                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37210              }
37211             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37212             this.expandBtn.on("click", this.expand, this);
37213             
37214         }
37215         
37216         if(this.collapseBtn){
37217             this.collapseBtn.setVisible(c.collapsible == true);
37218         }
37219         
37220         this.cmargins = c.cmargins || this.cmargins ||
37221                          (this.position == "west" || this.position == "east" ?
37222                              {top: 0, left: 2, right:2, bottom: 0} :
37223                              {top: 2, left: 0, right:0, bottom: 2});
37224         */
37225         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37226         
37227         
37228         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37229         
37230         this.autoScroll = c.autoScroll || false;
37231         
37232         
37233        
37234         
37235         this.duration = c.duration || .30;
37236         this.slideDuration = c.slideDuration || .45;
37237         this.config = c;
37238        
37239     },
37240     /**
37241      * Returns true if this region is currently visible.
37242      * @return {Boolean}
37243      */
37244     isVisible : function(){
37245         return this.visible;
37246     },
37247
37248     /**
37249      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37250      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37251      */
37252     //setCollapsedTitle : function(title){
37253     //    title = title || "&#160;";
37254      //   if(this.collapsedTitleTextEl){
37255       //      this.collapsedTitleTextEl.innerHTML = title;
37256        // }
37257     //},
37258
37259     getBox : function(){
37260         var b;
37261       //  if(!this.collapsed){
37262             b = this.el.getBox(false, true);
37263        // }else{
37264           //  b = this.collapsedEl.getBox(false, true);
37265         //}
37266         return b;
37267     },
37268
37269     getMargins : function(){
37270         return this.margins;
37271         //return this.collapsed ? this.cmargins : this.margins;
37272     },
37273 /*
37274     highlight : function(){
37275         this.el.addClass("x-layout-panel-dragover");
37276     },
37277
37278     unhighlight : function(){
37279         this.el.removeClass("x-layout-panel-dragover");
37280     },
37281 */
37282     updateBox : function(box)
37283     {
37284         if (!this.bodyEl) {
37285             return; // not rendered yet..
37286         }
37287         
37288         this.box = box;
37289         if(!this.collapsed){
37290             this.el.dom.style.left = box.x + "px";
37291             this.el.dom.style.top = box.y + "px";
37292             this.updateBody(box.width, box.height);
37293         }else{
37294             this.collapsedEl.dom.style.left = box.x + "px";
37295             this.collapsedEl.dom.style.top = box.y + "px";
37296             this.collapsedEl.setSize(box.width, box.height);
37297         }
37298         if(this.tabs){
37299             this.tabs.autoSizeTabs();
37300         }
37301     },
37302
37303     updateBody : function(w, h)
37304     {
37305         if(w !== null){
37306             this.el.setWidth(w);
37307             w -= this.el.getBorderWidth("rl");
37308             if(this.config.adjustments){
37309                 w += this.config.adjustments[0];
37310             }
37311         }
37312         if(h !== null && h > 0){
37313             this.el.setHeight(h);
37314             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37315             h -= this.el.getBorderWidth("tb");
37316             if(this.config.adjustments){
37317                 h += this.config.adjustments[1];
37318             }
37319             this.bodyEl.setHeight(h);
37320             if(this.tabs){
37321                 h = this.tabs.syncHeight(h);
37322             }
37323         }
37324         if(this.panelSize){
37325             w = w !== null ? w : this.panelSize.width;
37326             h = h !== null ? h : this.panelSize.height;
37327         }
37328         if(this.activePanel){
37329             var el = this.activePanel.getEl();
37330             w = w !== null ? w : el.getWidth();
37331             h = h !== null ? h : el.getHeight();
37332             this.panelSize = {width: w, height: h};
37333             this.activePanel.setSize(w, h);
37334         }
37335         if(Roo.isIE && this.tabs){
37336             this.tabs.el.repaint();
37337         }
37338     },
37339
37340     /**
37341      * Returns the container element for this region.
37342      * @return {Roo.Element}
37343      */
37344     getEl : function(){
37345         return this.el;
37346     },
37347
37348     /**
37349      * Hides this region.
37350      */
37351     hide : function(){
37352         //if(!this.collapsed){
37353             this.el.dom.style.left = "-2000px";
37354             this.el.hide();
37355         //}else{
37356          //   this.collapsedEl.dom.style.left = "-2000px";
37357          //   this.collapsedEl.hide();
37358        // }
37359         this.visible = false;
37360         this.fireEvent("visibilitychange", this, false);
37361     },
37362
37363     /**
37364      * Shows this region if it was previously hidden.
37365      */
37366     show : function(){
37367         //if(!this.collapsed){
37368             this.el.show();
37369         //}else{
37370         //    this.collapsedEl.show();
37371        // }
37372         this.visible = true;
37373         this.fireEvent("visibilitychange", this, true);
37374     },
37375 /*
37376     closeClicked : function(){
37377         if(this.activePanel){
37378             this.remove(this.activePanel);
37379         }
37380     },
37381
37382     collapseClick : function(e){
37383         if(this.isSlid){
37384            e.stopPropagation();
37385            this.slideIn();
37386         }else{
37387            e.stopPropagation();
37388            this.slideOut();
37389         }
37390     },
37391 */
37392     /**
37393      * Collapses this region.
37394      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37395      */
37396     /*
37397     collapse : function(skipAnim, skipCheck = false){
37398         if(this.collapsed) {
37399             return;
37400         }
37401         
37402         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37403             
37404             this.collapsed = true;
37405             if(this.split){
37406                 this.split.el.hide();
37407             }
37408             if(this.config.animate && skipAnim !== true){
37409                 this.fireEvent("invalidated", this);
37410                 this.animateCollapse();
37411             }else{
37412                 this.el.setLocation(-20000,-20000);
37413                 this.el.hide();
37414                 this.collapsedEl.show();
37415                 this.fireEvent("collapsed", this);
37416                 this.fireEvent("invalidated", this);
37417             }
37418         }
37419         
37420     },
37421 */
37422     animateCollapse : function(){
37423         // overridden
37424     },
37425
37426     /**
37427      * Expands this region if it was previously collapsed.
37428      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37429      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37430      */
37431     /*
37432     expand : function(e, skipAnim){
37433         if(e) {
37434             e.stopPropagation();
37435         }
37436         if(!this.collapsed || this.el.hasActiveFx()) {
37437             return;
37438         }
37439         if(this.isSlid){
37440             this.afterSlideIn();
37441             skipAnim = true;
37442         }
37443         this.collapsed = false;
37444         if(this.config.animate && skipAnim !== true){
37445             this.animateExpand();
37446         }else{
37447             this.el.show();
37448             if(this.split){
37449                 this.split.el.show();
37450             }
37451             this.collapsedEl.setLocation(-2000,-2000);
37452             this.collapsedEl.hide();
37453             this.fireEvent("invalidated", this);
37454             this.fireEvent("expanded", this);
37455         }
37456     },
37457 */
37458     animateExpand : function(){
37459         // overridden
37460     },
37461
37462     initTabs : function()
37463     {
37464         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37465         
37466         var ts = new Roo.bootstrap.panel.Tabs({
37467             el: this.bodyEl.dom,
37468             region : this,
37469             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37470             disableTooltips: this.config.disableTabTips,
37471             toolbar : this.config.toolbar
37472         });
37473         
37474         if(this.config.hideTabs){
37475             ts.stripWrap.setDisplayed(false);
37476         }
37477         this.tabs = ts;
37478         ts.resizeTabs = this.config.resizeTabs === true;
37479         ts.minTabWidth = this.config.minTabWidth || 40;
37480         ts.maxTabWidth = this.config.maxTabWidth || 250;
37481         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37482         ts.monitorResize = false;
37483         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37484         ts.bodyEl.addClass('roo-layout-tabs-body');
37485         this.panels.each(this.initPanelAsTab, this);
37486     },
37487
37488     initPanelAsTab : function(panel){
37489         var ti = this.tabs.addTab(
37490             panel.getEl().id,
37491             panel.getTitle(),
37492             null,
37493             this.config.closeOnTab && panel.isClosable(),
37494             panel.tpl
37495         );
37496         if(panel.tabTip !== undefined){
37497             ti.setTooltip(panel.tabTip);
37498         }
37499         ti.on("activate", function(){
37500               this.setActivePanel(panel);
37501         }, this);
37502         
37503         if(this.config.closeOnTab){
37504             ti.on("beforeclose", function(t, e){
37505                 e.cancel = true;
37506                 this.remove(panel);
37507             }, this);
37508         }
37509         
37510         panel.tabItem = ti;
37511         
37512         return ti;
37513     },
37514
37515     updatePanelTitle : function(panel, title)
37516     {
37517         if(this.activePanel == panel){
37518             this.updateTitle(title);
37519         }
37520         if(this.tabs){
37521             var ti = this.tabs.getTab(panel.getEl().id);
37522             ti.setText(title);
37523             if(panel.tabTip !== undefined){
37524                 ti.setTooltip(panel.tabTip);
37525             }
37526         }
37527     },
37528
37529     updateTitle : function(title){
37530         if(this.titleTextEl && !this.config.title){
37531             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37532         }
37533     },
37534
37535     setActivePanel : function(panel)
37536     {
37537         panel = this.getPanel(panel);
37538         if(this.activePanel && this.activePanel != panel){
37539             if(this.activePanel.setActiveState(false) === false){
37540                 return;
37541             }
37542         }
37543         this.activePanel = panel;
37544         panel.setActiveState(true);
37545         if(this.panelSize){
37546             panel.setSize(this.panelSize.width, this.panelSize.height);
37547         }
37548         if(this.closeBtn){
37549             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37550         }
37551         this.updateTitle(panel.getTitle());
37552         if(this.tabs){
37553             this.fireEvent("invalidated", this);
37554         }
37555         this.fireEvent("panelactivated", this, panel);
37556     },
37557
37558     /**
37559      * Shows the specified panel.
37560      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37561      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37562      */
37563     showPanel : function(panel)
37564     {
37565         panel = this.getPanel(panel);
37566         if(panel){
37567             if(this.tabs){
37568                 var tab = this.tabs.getTab(panel.getEl().id);
37569                 if(tab.isHidden()){
37570                     this.tabs.unhideTab(tab.id);
37571                 }
37572                 tab.activate();
37573             }else{
37574                 this.setActivePanel(panel);
37575             }
37576         }
37577         return panel;
37578     },
37579
37580     /**
37581      * Get the active panel for this region.
37582      * @return {Roo.ContentPanel} The active panel or null
37583      */
37584     getActivePanel : function(){
37585         return this.activePanel;
37586     },
37587
37588     validateVisibility : function(){
37589         if(this.panels.getCount() < 1){
37590             this.updateTitle("&#160;");
37591             this.closeBtn.hide();
37592             this.hide();
37593         }else{
37594             if(!this.isVisible()){
37595                 this.show();
37596             }
37597         }
37598     },
37599
37600     /**
37601      * Adds the passed ContentPanel(s) to this region.
37602      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37603      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37604      */
37605     add : function(panel)
37606     {
37607         if(arguments.length > 1){
37608             for(var i = 0, len = arguments.length; i < len; i++) {
37609                 this.add(arguments[i]);
37610             }
37611             return null;
37612         }
37613         
37614         // if we have not been rendered yet, then we can not really do much of this..
37615         if (!this.bodyEl) {
37616             this.unrendered_panels.push(panel);
37617             return panel;
37618         }
37619         
37620         
37621         
37622         
37623         if(this.hasPanel(panel)){
37624             this.showPanel(panel);
37625             return panel;
37626         }
37627         panel.setRegion(this);
37628         this.panels.add(panel);
37629        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37630             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37631             // and hide them... ???
37632             this.bodyEl.dom.appendChild(panel.getEl().dom);
37633             if(panel.background !== true){
37634                 this.setActivePanel(panel);
37635             }
37636             this.fireEvent("paneladded", this, panel);
37637             return panel;
37638         }
37639         */
37640         if(!this.tabs){
37641             this.initTabs();
37642         }else{
37643             this.initPanelAsTab(panel);
37644         }
37645         
37646         
37647         if(panel.background !== true){
37648             this.tabs.activate(panel.getEl().id);
37649         }
37650         this.fireEvent("paneladded", this, panel);
37651         return panel;
37652     },
37653
37654     /**
37655      * Hides the tab for the specified panel.
37656      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37657      */
37658     hidePanel : function(panel){
37659         if(this.tabs && (panel = this.getPanel(panel))){
37660             this.tabs.hideTab(panel.getEl().id);
37661         }
37662     },
37663
37664     /**
37665      * Unhides the tab for a previously hidden panel.
37666      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37667      */
37668     unhidePanel : function(panel){
37669         if(this.tabs && (panel = this.getPanel(panel))){
37670             this.tabs.unhideTab(panel.getEl().id);
37671         }
37672     },
37673
37674     clearPanels : function(){
37675         while(this.panels.getCount() > 0){
37676              this.remove(this.panels.first());
37677         }
37678     },
37679
37680     /**
37681      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37682      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37683      * @param {Boolean} preservePanel Overrides the config preservePanel option
37684      * @return {Roo.ContentPanel} The panel that was removed
37685      */
37686     remove : function(panel, preservePanel)
37687     {
37688         panel = this.getPanel(panel);
37689         if(!panel){
37690             return null;
37691         }
37692         var e = {};
37693         this.fireEvent("beforeremove", this, panel, e);
37694         if(e.cancel === true){
37695             return null;
37696         }
37697         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37698         var panelId = panel.getId();
37699         this.panels.removeKey(panelId);
37700         if(preservePanel){
37701             document.body.appendChild(panel.getEl().dom);
37702         }
37703         if(this.tabs){
37704             this.tabs.removeTab(panel.getEl().id);
37705         }else if (!preservePanel){
37706             this.bodyEl.dom.removeChild(panel.getEl().dom);
37707         }
37708         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37709             var p = this.panels.first();
37710             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37711             tempEl.appendChild(p.getEl().dom);
37712             this.bodyEl.update("");
37713             this.bodyEl.dom.appendChild(p.getEl().dom);
37714             tempEl = null;
37715             this.updateTitle(p.getTitle());
37716             this.tabs = null;
37717             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37718             this.setActivePanel(p);
37719         }
37720         panel.setRegion(null);
37721         if(this.activePanel == panel){
37722             this.activePanel = null;
37723         }
37724         if(this.config.autoDestroy !== false && preservePanel !== true){
37725             try{panel.destroy();}catch(e){}
37726         }
37727         this.fireEvent("panelremoved", this, panel);
37728         return panel;
37729     },
37730
37731     /**
37732      * Returns the TabPanel component used by this region
37733      * @return {Roo.TabPanel}
37734      */
37735     getTabs : function(){
37736         return this.tabs;
37737     },
37738
37739     createTool : function(parentEl, className){
37740         var btn = Roo.DomHelper.append(parentEl, {
37741             tag: "div",
37742             cls: "x-layout-tools-button",
37743             children: [ {
37744                 tag: "div",
37745                 cls: "roo-layout-tools-button-inner " + className,
37746                 html: "&#160;"
37747             }]
37748         }, true);
37749         btn.addClassOnOver("roo-layout-tools-button-over");
37750         return btn;
37751     }
37752 });/*
37753  * Based on:
37754  * Ext JS Library 1.1.1
37755  * Copyright(c) 2006-2007, Ext JS, LLC.
37756  *
37757  * Originally Released Under LGPL - original licence link has changed is not relivant.
37758  *
37759  * Fork - LGPL
37760  * <script type="text/javascript">
37761  */
37762  
37763
37764
37765 /**
37766  * @class Roo.SplitLayoutRegion
37767  * @extends Roo.LayoutRegion
37768  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37769  */
37770 Roo.bootstrap.layout.Split = function(config){
37771     this.cursor = config.cursor;
37772     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37773 };
37774
37775 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37776 {
37777     splitTip : "Drag to resize.",
37778     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37779     useSplitTips : false,
37780
37781     applyConfig : function(config){
37782         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37783     },
37784     
37785     onRender : function(ctr,pos) {
37786         
37787         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37788         if(!this.config.split){
37789             return;
37790         }
37791         if(!this.split){
37792             
37793             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37794                             tag: "div",
37795                             id: this.el.id + "-split",
37796                             cls: "roo-layout-split roo-layout-split-"+this.position,
37797                             html: "&#160;"
37798             });
37799             /** The SplitBar for this region 
37800             * @type Roo.SplitBar */
37801             // does not exist yet...
37802             Roo.log([this.position, this.orientation]);
37803             
37804             this.split = new Roo.bootstrap.SplitBar({
37805                 dragElement : splitEl,
37806                 resizingElement: this.el,
37807                 orientation : this.orientation
37808             });
37809             
37810             this.split.on("moved", this.onSplitMove, this);
37811             this.split.useShim = this.config.useShim === true;
37812             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37813             if(this.useSplitTips){
37814                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37815             }
37816             //if(config.collapsible){
37817             //    this.split.el.on("dblclick", this.collapse,  this);
37818             //}
37819         }
37820         if(typeof this.config.minSize != "undefined"){
37821             this.split.minSize = this.config.minSize;
37822         }
37823         if(typeof this.config.maxSize != "undefined"){
37824             this.split.maxSize = this.config.maxSize;
37825         }
37826         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37827             this.hideSplitter();
37828         }
37829         
37830     },
37831
37832     getHMaxSize : function(){
37833          var cmax = this.config.maxSize || 10000;
37834          var center = this.mgr.getRegion("center");
37835          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37836     },
37837
37838     getVMaxSize : function(){
37839          var cmax = this.config.maxSize || 10000;
37840          var center = this.mgr.getRegion("center");
37841          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37842     },
37843
37844     onSplitMove : function(split, newSize){
37845         this.fireEvent("resized", this, newSize);
37846     },
37847     
37848     /** 
37849      * Returns the {@link Roo.SplitBar} for this region.
37850      * @return {Roo.SplitBar}
37851      */
37852     getSplitBar : function(){
37853         return this.split;
37854     },
37855     
37856     hide : function(){
37857         this.hideSplitter();
37858         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37859     },
37860
37861     hideSplitter : function(){
37862         if(this.split){
37863             this.split.el.setLocation(-2000,-2000);
37864             this.split.el.hide();
37865         }
37866     },
37867
37868     show : function(){
37869         if(this.split){
37870             this.split.el.show();
37871         }
37872         Roo.bootstrap.layout.Split.superclass.show.call(this);
37873     },
37874     
37875     beforeSlide: function(){
37876         if(Roo.isGecko){// firefox overflow auto bug workaround
37877             this.bodyEl.clip();
37878             if(this.tabs) {
37879                 this.tabs.bodyEl.clip();
37880             }
37881             if(this.activePanel){
37882                 this.activePanel.getEl().clip();
37883                 
37884                 if(this.activePanel.beforeSlide){
37885                     this.activePanel.beforeSlide();
37886                 }
37887             }
37888         }
37889     },
37890     
37891     afterSlide : function(){
37892         if(Roo.isGecko){// firefox overflow auto bug workaround
37893             this.bodyEl.unclip();
37894             if(this.tabs) {
37895                 this.tabs.bodyEl.unclip();
37896             }
37897             if(this.activePanel){
37898                 this.activePanel.getEl().unclip();
37899                 if(this.activePanel.afterSlide){
37900                     this.activePanel.afterSlide();
37901                 }
37902             }
37903         }
37904     },
37905
37906     initAutoHide : function(){
37907         if(this.autoHide !== false){
37908             if(!this.autoHideHd){
37909                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37910                 this.autoHideHd = {
37911                     "mouseout": function(e){
37912                         if(!e.within(this.el, true)){
37913                             st.delay(500);
37914                         }
37915                     },
37916                     "mouseover" : function(e){
37917                         st.cancel();
37918                     },
37919                     scope : this
37920                 };
37921             }
37922             this.el.on(this.autoHideHd);
37923         }
37924     },
37925
37926     clearAutoHide : function(){
37927         if(this.autoHide !== false){
37928             this.el.un("mouseout", this.autoHideHd.mouseout);
37929             this.el.un("mouseover", this.autoHideHd.mouseover);
37930         }
37931     },
37932
37933     clearMonitor : function(){
37934         Roo.get(document).un("click", this.slideInIf, this);
37935     },
37936
37937     // these names are backwards but not changed for compat
37938     slideOut : function(){
37939         if(this.isSlid || this.el.hasActiveFx()){
37940             return;
37941         }
37942         this.isSlid = true;
37943         if(this.collapseBtn){
37944             this.collapseBtn.hide();
37945         }
37946         this.closeBtnState = this.closeBtn.getStyle('display');
37947         this.closeBtn.hide();
37948         if(this.stickBtn){
37949             this.stickBtn.show();
37950         }
37951         this.el.show();
37952         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37953         this.beforeSlide();
37954         this.el.setStyle("z-index", 10001);
37955         this.el.slideIn(this.getSlideAnchor(), {
37956             callback: function(){
37957                 this.afterSlide();
37958                 this.initAutoHide();
37959                 Roo.get(document).on("click", this.slideInIf, this);
37960                 this.fireEvent("slideshow", this);
37961             },
37962             scope: this,
37963             block: true
37964         });
37965     },
37966
37967     afterSlideIn : function(){
37968         this.clearAutoHide();
37969         this.isSlid = false;
37970         this.clearMonitor();
37971         this.el.setStyle("z-index", "");
37972         if(this.collapseBtn){
37973             this.collapseBtn.show();
37974         }
37975         this.closeBtn.setStyle('display', this.closeBtnState);
37976         if(this.stickBtn){
37977             this.stickBtn.hide();
37978         }
37979         this.fireEvent("slidehide", this);
37980     },
37981
37982     slideIn : function(cb){
37983         if(!this.isSlid || this.el.hasActiveFx()){
37984             Roo.callback(cb);
37985             return;
37986         }
37987         this.isSlid = false;
37988         this.beforeSlide();
37989         this.el.slideOut(this.getSlideAnchor(), {
37990             callback: function(){
37991                 this.el.setLeftTop(-10000, -10000);
37992                 this.afterSlide();
37993                 this.afterSlideIn();
37994                 Roo.callback(cb);
37995             },
37996             scope: this,
37997             block: true
37998         });
37999     },
38000     
38001     slideInIf : function(e){
38002         if(!e.within(this.el)){
38003             this.slideIn();
38004         }
38005     },
38006
38007     animateCollapse : function(){
38008         this.beforeSlide();
38009         this.el.setStyle("z-index", 20000);
38010         var anchor = this.getSlideAnchor();
38011         this.el.slideOut(anchor, {
38012             callback : function(){
38013                 this.el.setStyle("z-index", "");
38014                 this.collapsedEl.slideIn(anchor, {duration:.3});
38015                 this.afterSlide();
38016                 this.el.setLocation(-10000,-10000);
38017                 this.el.hide();
38018                 this.fireEvent("collapsed", this);
38019             },
38020             scope: this,
38021             block: true
38022         });
38023     },
38024
38025     animateExpand : function(){
38026         this.beforeSlide();
38027         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38028         this.el.setStyle("z-index", 20000);
38029         this.collapsedEl.hide({
38030             duration:.1
38031         });
38032         this.el.slideIn(this.getSlideAnchor(), {
38033             callback : function(){
38034                 this.el.setStyle("z-index", "");
38035                 this.afterSlide();
38036                 if(this.split){
38037                     this.split.el.show();
38038                 }
38039                 this.fireEvent("invalidated", this);
38040                 this.fireEvent("expanded", this);
38041             },
38042             scope: this,
38043             block: true
38044         });
38045     },
38046
38047     anchors : {
38048         "west" : "left",
38049         "east" : "right",
38050         "north" : "top",
38051         "south" : "bottom"
38052     },
38053
38054     sanchors : {
38055         "west" : "l",
38056         "east" : "r",
38057         "north" : "t",
38058         "south" : "b"
38059     },
38060
38061     canchors : {
38062         "west" : "tl-tr",
38063         "east" : "tr-tl",
38064         "north" : "tl-bl",
38065         "south" : "bl-tl"
38066     },
38067
38068     getAnchor : function(){
38069         return this.anchors[this.position];
38070     },
38071
38072     getCollapseAnchor : function(){
38073         return this.canchors[this.position];
38074     },
38075
38076     getSlideAnchor : function(){
38077         return this.sanchors[this.position];
38078     },
38079
38080     getAlignAdj : function(){
38081         var cm = this.cmargins;
38082         switch(this.position){
38083             case "west":
38084                 return [0, 0];
38085             break;
38086             case "east":
38087                 return [0, 0];
38088             break;
38089             case "north":
38090                 return [0, 0];
38091             break;
38092             case "south":
38093                 return [0, 0];
38094             break;
38095         }
38096     },
38097
38098     getExpandAdj : function(){
38099         var c = this.collapsedEl, cm = this.cmargins;
38100         switch(this.position){
38101             case "west":
38102                 return [-(cm.right+c.getWidth()+cm.left), 0];
38103             break;
38104             case "east":
38105                 return [cm.right+c.getWidth()+cm.left, 0];
38106             break;
38107             case "north":
38108                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38109             break;
38110             case "south":
38111                 return [0, cm.top+cm.bottom+c.getHeight()];
38112             break;
38113         }
38114     }
38115 });/*
38116  * Based on:
38117  * Ext JS Library 1.1.1
38118  * Copyright(c) 2006-2007, Ext JS, LLC.
38119  *
38120  * Originally Released Under LGPL - original licence link has changed is not relivant.
38121  *
38122  * Fork - LGPL
38123  * <script type="text/javascript">
38124  */
38125 /*
38126  * These classes are private internal classes
38127  */
38128 Roo.bootstrap.layout.Center = function(config){
38129     config.region = "center";
38130     Roo.bootstrap.layout.Region.call(this, config);
38131     this.visible = true;
38132     this.minWidth = config.minWidth || 20;
38133     this.minHeight = config.minHeight || 20;
38134 };
38135
38136 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38137     hide : function(){
38138         // center panel can't be hidden
38139     },
38140     
38141     show : function(){
38142         // center panel can't be hidden
38143     },
38144     
38145     getMinWidth: function(){
38146         return this.minWidth;
38147     },
38148     
38149     getMinHeight: function(){
38150         return this.minHeight;
38151     }
38152 });
38153
38154
38155
38156
38157  
38158
38159
38160
38161
38162
38163
38164 Roo.bootstrap.layout.North = function(config)
38165 {
38166     config.region = 'north';
38167     config.cursor = 'n-resize';
38168     
38169     Roo.bootstrap.layout.Split.call(this, config);
38170     
38171     
38172     if(this.split){
38173         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38174         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38175         this.split.el.addClass("roo-layout-split-v");
38176     }
38177     var size = config.initialSize || config.height;
38178     if(typeof size != "undefined"){
38179         this.el.setHeight(size);
38180     }
38181 };
38182 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38183 {
38184     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38185     
38186     
38187     
38188     getBox : function(){
38189         if(this.collapsed){
38190             return this.collapsedEl.getBox();
38191         }
38192         var box = this.el.getBox();
38193         if(this.split){
38194             box.height += this.split.el.getHeight();
38195         }
38196         return box;
38197     },
38198     
38199     updateBox : function(box){
38200         if(this.split && !this.collapsed){
38201             box.height -= this.split.el.getHeight();
38202             this.split.el.setLeft(box.x);
38203             this.split.el.setTop(box.y+box.height);
38204             this.split.el.setWidth(box.width);
38205         }
38206         if(this.collapsed){
38207             this.updateBody(box.width, null);
38208         }
38209         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38210     }
38211 });
38212
38213
38214
38215
38216
38217 Roo.bootstrap.layout.South = function(config){
38218     config.region = 'south';
38219     config.cursor = 's-resize';
38220     Roo.bootstrap.layout.Split.call(this, config);
38221     if(this.split){
38222         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38223         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38224         this.split.el.addClass("roo-layout-split-v");
38225     }
38226     var size = config.initialSize || config.height;
38227     if(typeof size != "undefined"){
38228         this.el.setHeight(size);
38229     }
38230 };
38231
38232 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38233     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38234     getBox : function(){
38235         if(this.collapsed){
38236             return this.collapsedEl.getBox();
38237         }
38238         var box = this.el.getBox();
38239         if(this.split){
38240             var sh = this.split.el.getHeight();
38241             box.height += sh;
38242             box.y -= sh;
38243         }
38244         return box;
38245     },
38246     
38247     updateBox : function(box){
38248         if(this.split && !this.collapsed){
38249             var sh = this.split.el.getHeight();
38250             box.height -= sh;
38251             box.y += sh;
38252             this.split.el.setLeft(box.x);
38253             this.split.el.setTop(box.y-sh);
38254             this.split.el.setWidth(box.width);
38255         }
38256         if(this.collapsed){
38257             this.updateBody(box.width, null);
38258         }
38259         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38260     }
38261 });
38262
38263 Roo.bootstrap.layout.East = function(config){
38264     config.region = "east";
38265     config.cursor = "e-resize";
38266     Roo.bootstrap.layout.Split.call(this, config);
38267     if(this.split){
38268         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38269         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38270         this.split.el.addClass("roo-layout-split-h");
38271     }
38272     var size = config.initialSize || config.width;
38273     if(typeof size != "undefined"){
38274         this.el.setWidth(size);
38275     }
38276 };
38277 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38278     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38279     getBox : function(){
38280         if(this.collapsed){
38281             return this.collapsedEl.getBox();
38282         }
38283         var box = this.el.getBox();
38284         if(this.split){
38285             var sw = this.split.el.getWidth();
38286             box.width += sw;
38287             box.x -= sw;
38288         }
38289         return box;
38290     },
38291
38292     updateBox : function(box){
38293         if(this.split && !this.collapsed){
38294             var sw = this.split.el.getWidth();
38295             box.width -= sw;
38296             this.split.el.setLeft(box.x);
38297             this.split.el.setTop(box.y);
38298             this.split.el.setHeight(box.height);
38299             box.x += sw;
38300         }
38301         if(this.collapsed){
38302             this.updateBody(null, box.height);
38303         }
38304         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38305     }
38306 });
38307
38308 Roo.bootstrap.layout.West = function(config){
38309     config.region = "west";
38310     config.cursor = "w-resize";
38311     
38312     Roo.bootstrap.layout.Split.call(this, config);
38313     if(this.split){
38314         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38315         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38316         this.split.el.addClass("roo-layout-split-h");
38317     }
38318     
38319 };
38320 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38321     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38322     
38323     onRender: function(ctr, pos)
38324     {
38325         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38326         var size = this.config.initialSize || this.config.width;
38327         if(typeof size != "undefined"){
38328             this.el.setWidth(size);
38329         }
38330     },
38331     
38332     getBox : function(){
38333         if(this.collapsed){
38334             return this.collapsedEl.getBox();
38335         }
38336         var box = this.el.getBox();
38337         if(this.split){
38338             box.width += this.split.el.getWidth();
38339         }
38340         return box;
38341     },
38342     
38343     updateBox : function(box){
38344         if(this.split && !this.collapsed){
38345             var sw = this.split.el.getWidth();
38346             box.width -= sw;
38347             this.split.el.setLeft(box.x+box.width);
38348             this.split.el.setTop(box.y);
38349             this.split.el.setHeight(box.height);
38350         }
38351         if(this.collapsed){
38352             this.updateBody(null, box.height);
38353         }
38354         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38355     }
38356 });Roo.namespace("Roo.bootstrap.panel");/*
38357  * Based on:
38358  * Ext JS Library 1.1.1
38359  * Copyright(c) 2006-2007, Ext JS, LLC.
38360  *
38361  * Originally Released Under LGPL - original licence link has changed is not relivant.
38362  *
38363  * Fork - LGPL
38364  * <script type="text/javascript">
38365  */
38366 /**
38367  * @class Roo.ContentPanel
38368  * @extends Roo.util.Observable
38369  * A basic ContentPanel element.
38370  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38371  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38372  * @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
38373  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38374  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38375  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38376  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38377  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38378  * @cfg {String} title          The title for this panel
38379  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38380  * @cfg {String} url            Calls {@link #setUrl} with this value
38381  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38382  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38383  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38384  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38385  * @cfg {Boolean} badges render the badges
38386
38387  * @constructor
38388  * Create a new ContentPanel.
38389  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38390  * @param {String/Object} config A string to set only the title or a config object
38391  * @param {String} content (optional) Set the HTML content for this panel
38392  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38393  */
38394 Roo.bootstrap.panel.Content = function( config){
38395     
38396     this.tpl = config.tpl || false;
38397     
38398     var el = config.el;
38399     var content = config.content;
38400
38401     if(config.autoCreate){ // xtype is available if this is called from factory
38402         el = Roo.id();
38403     }
38404     this.el = Roo.get(el);
38405     if(!this.el && config && config.autoCreate){
38406         if(typeof config.autoCreate == "object"){
38407             if(!config.autoCreate.id){
38408                 config.autoCreate.id = config.id||el;
38409             }
38410             this.el = Roo.DomHelper.append(document.body,
38411                         config.autoCreate, true);
38412         }else{
38413             var elcfg =  {   tag: "div",
38414                             cls: "roo-layout-inactive-content",
38415                             id: config.id||el
38416                             };
38417             if (config.html) {
38418                 elcfg.html = config.html;
38419                 
38420             }
38421                         
38422             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38423         }
38424     } 
38425     this.closable = false;
38426     this.loaded = false;
38427     this.active = false;
38428    
38429       
38430     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38431         
38432         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38433         
38434         this.wrapEl = this.el; //this.el.wrap();
38435         var ti = [];
38436         if (config.toolbar.items) {
38437             ti = config.toolbar.items ;
38438             delete config.toolbar.items ;
38439         }
38440         
38441         var nitems = [];
38442         this.toolbar.render(this.wrapEl, 'before');
38443         for(var i =0;i < ti.length;i++) {
38444           //  Roo.log(['add child', items[i]]);
38445             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38446         }
38447         this.toolbar.items = nitems;
38448         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38449         delete config.toolbar;
38450         
38451     }
38452     /*
38453     // xtype created footer. - not sure if will work as we normally have to render first..
38454     if (this.footer && !this.footer.el && this.footer.xtype) {
38455         if (!this.wrapEl) {
38456             this.wrapEl = this.el.wrap();
38457         }
38458     
38459         this.footer.container = this.wrapEl.createChild();
38460          
38461         this.footer = Roo.factory(this.footer, Roo);
38462         
38463     }
38464     */
38465     
38466      if(typeof config == "string"){
38467         this.title = config;
38468     }else{
38469         Roo.apply(this, config);
38470     }
38471     
38472     if(this.resizeEl){
38473         this.resizeEl = Roo.get(this.resizeEl, true);
38474     }else{
38475         this.resizeEl = this.el;
38476     }
38477     // handle view.xtype
38478     
38479  
38480     
38481     
38482     this.addEvents({
38483         /**
38484          * @event activate
38485          * Fires when this panel is activated. 
38486          * @param {Roo.ContentPanel} this
38487          */
38488         "activate" : true,
38489         /**
38490          * @event deactivate
38491          * Fires when this panel is activated. 
38492          * @param {Roo.ContentPanel} this
38493          */
38494         "deactivate" : true,
38495
38496         /**
38497          * @event resize
38498          * Fires when this panel is resized if fitToFrame is true.
38499          * @param {Roo.ContentPanel} this
38500          * @param {Number} width The width after any component adjustments
38501          * @param {Number} height The height after any component adjustments
38502          */
38503         "resize" : true,
38504         
38505          /**
38506          * @event render
38507          * Fires when this tab is created
38508          * @param {Roo.ContentPanel} this
38509          */
38510         "render" : true
38511         
38512         
38513         
38514     });
38515     
38516
38517     
38518     
38519     if(this.autoScroll){
38520         this.resizeEl.setStyle("overflow", "auto");
38521     } else {
38522         // fix randome scrolling
38523         //this.el.on('scroll', function() {
38524         //    Roo.log('fix random scolling');
38525         //    this.scrollTo('top',0); 
38526         //});
38527     }
38528     content = content || this.content;
38529     if(content){
38530         this.setContent(content);
38531     }
38532     if(config && config.url){
38533         this.setUrl(this.url, this.params, this.loadOnce);
38534     }
38535     
38536     
38537     
38538     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38539     
38540     if (this.view && typeof(this.view.xtype) != 'undefined') {
38541         this.view.el = this.el.appendChild(document.createElement("div"));
38542         this.view = Roo.factory(this.view); 
38543         this.view.render  &&  this.view.render(false, '');  
38544     }
38545     
38546     
38547     this.fireEvent('render', this);
38548 };
38549
38550 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38551     
38552     tabTip : '',
38553     
38554     setRegion : function(region){
38555         this.region = region;
38556         this.setActiveClass(region && !this.background);
38557     },
38558     
38559     
38560     setActiveClass: function(state)
38561     {
38562         if(state){
38563            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38564            this.el.setStyle('position','relative');
38565         }else{
38566            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38567            this.el.setStyle('position', 'absolute');
38568         } 
38569     },
38570     
38571     /**
38572      * Returns the toolbar for this Panel if one was configured. 
38573      * @return {Roo.Toolbar} 
38574      */
38575     getToolbar : function(){
38576         return this.toolbar;
38577     },
38578     
38579     setActiveState : function(active)
38580     {
38581         this.active = active;
38582         this.setActiveClass(active);
38583         if(!active){
38584             if(this.fireEvent("deactivate", this) === false){
38585                 return false;
38586             }
38587             return true;
38588         }
38589         this.fireEvent("activate", this);
38590         return true;
38591     },
38592     /**
38593      * Updates this panel's element
38594      * @param {String} content The new content
38595      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38596     */
38597     setContent : function(content, loadScripts){
38598         this.el.update(content, loadScripts);
38599     },
38600
38601     ignoreResize : function(w, h){
38602         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38603             return true;
38604         }else{
38605             this.lastSize = {width: w, height: h};
38606             return false;
38607         }
38608     },
38609     /**
38610      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38611      * @return {Roo.UpdateManager} The UpdateManager
38612      */
38613     getUpdateManager : function(){
38614         return this.el.getUpdateManager();
38615     },
38616      /**
38617      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38618      * @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:
38619 <pre><code>
38620 panel.load({
38621     url: "your-url.php",
38622     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38623     callback: yourFunction,
38624     scope: yourObject, //(optional scope)
38625     discardUrl: false,
38626     nocache: false,
38627     text: "Loading...",
38628     timeout: 30,
38629     scripts: false
38630 });
38631 </code></pre>
38632      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38633      * 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.
38634      * @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}
38635      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38636      * @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.
38637      * @return {Roo.ContentPanel} this
38638      */
38639     load : function(){
38640         var um = this.el.getUpdateManager();
38641         um.update.apply(um, arguments);
38642         return this;
38643     },
38644
38645
38646     /**
38647      * 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.
38648      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38649      * @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)
38650      * @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)
38651      * @return {Roo.UpdateManager} The UpdateManager
38652      */
38653     setUrl : function(url, params, loadOnce){
38654         if(this.refreshDelegate){
38655             this.removeListener("activate", this.refreshDelegate);
38656         }
38657         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38658         this.on("activate", this.refreshDelegate);
38659         return this.el.getUpdateManager();
38660     },
38661     
38662     _handleRefresh : function(url, params, loadOnce){
38663         if(!loadOnce || !this.loaded){
38664             var updater = this.el.getUpdateManager();
38665             updater.update(url, params, this._setLoaded.createDelegate(this));
38666         }
38667     },
38668     
38669     _setLoaded : function(){
38670         this.loaded = true;
38671     }, 
38672     
38673     /**
38674      * Returns this panel's id
38675      * @return {String} 
38676      */
38677     getId : function(){
38678         return this.el.id;
38679     },
38680     
38681     /** 
38682      * Returns this panel's element - used by regiosn to add.
38683      * @return {Roo.Element} 
38684      */
38685     getEl : function(){
38686         return this.wrapEl || this.el;
38687     },
38688     
38689    
38690     
38691     adjustForComponents : function(width, height)
38692     {
38693         //Roo.log('adjustForComponents ');
38694         if(this.resizeEl != this.el){
38695             width -= this.el.getFrameWidth('lr');
38696             height -= this.el.getFrameWidth('tb');
38697         }
38698         if(this.toolbar){
38699             var te = this.toolbar.getEl();
38700             te.setWidth(width);
38701             height -= te.getHeight();
38702         }
38703         if(this.footer){
38704             var te = this.footer.getEl();
38705             te.setWidth(width);
38706             height -= te.getHeight();
38707         }
38708         
38709         
38710         if(this.adjustments){
38711             width += this.adjustments[0];
38712             height += this.adjustments[1];
38713         }
38714         return {"width": width, "height": height};
38715     },
38716     
38717     setSize : function(width, height){
38718         if(this.fitToFrame && !this.ignoreResize(width, height)){
38719             if(this.fitContainer && this.resizeEl != this.el){
38720                 this.el.setSize(width, height);
38721             }
38722             var size = this.adjustForComponents(width, height);
38723             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38724             this.fireEvent('resize', this, size.width, size.height);
38725         }
38726     },
38727     
38728     /**
38729      * Returns this panel's title
38730      * @return {String} 
38731      */
38732     getTitle : function(){
38733         
38734         if (typeof(this.title) != 'object') {
38735             return this.title;
38736         }
38737         
38738         var t = '';
38739         for (var k in this.title) {
38740             if (!this.title.hasOwnProperty(k)) {
38741                 continue;
38742             }
38743             
38744             if (k.indexOf('-') >= 0) {
38745                 var s = k.split('-');
38746                 for (var i = 0; i<s.length; i++) {
38747                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38748                 }
38749             } else {
38750                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38751             }
38752         }
38753         return t;
38754     },
38755     
38756     /**
38757      * Set this panel's title
38758      * @param {String} title
38759      */
38760     setTitle : function(title){
38761         this.title = title;
38762         if(this.region){
38763             this.region.updatePanelTitle(this, title);
38764         }
38765     },
38766     
38767     /**
38768      * Returns true is this panel was configured to be closable
38769      * @return {Boolean} 
38770      */
38771     isClosable : function(){
38772         return this.closable;
38773     },
38774     
38775     beforeSlide : function(){
38776         this.el.clip();
38777         this.resizeEl.clip();
38778     },
38779     
38780     afterSlide : function(){
38781         this.el.unclip();
38782         this.resizeEl.unclip();
38783     },
38784     
38785     /**
38786      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38787      *   Will fail silently if the {@link #setUrl} method has not been called.
38788      *   This does not activate the panel, just updates its content.
38789      */
38790     refresh : function(){
38791         if(this.refreshDelegate){
38792            this.loaded = false;
38793            this.refreshDelegate();
38794         }
38795     },
38796     
38797     /**
38798      * Destroys this panel
38799      */
38800     destroy : function(){
38801         this.el.removeAllListeners();
38802         var tempEl = document.createElement("span");
38803         tempEl.appendChild(this.el.dom);
38804         tempEl.innerHTML = "";
38805         this.el.remove();
38806         this.el = null;
38807     },
38808     
38809     /**
38810      * form - if the content panel contains a form - this is a reference to it.
38811      * @type {Roo.form.Form}
38812      */
38813     form : false,
38814     /**
38815      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38816      *    This contains a reference to it.
38817      * @type {Roo.View}
38818      */
38819     view : false,
38820     
38821       /**
38822      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38823      * <pre><code>
38824
38825 layout.addxtype({
38826        xtype : 'Form',
38827        items: [ .... ]
38828    }
38829 );
38830
38831 </code></pre>
38832      * @param {Object} cfg Xtype definition of item to add.
38833      */
38834     
38835     
38836     getChildContainer: function () {
38837         return this.getEl();
38838     }
38839     
38840     
38841     /*
38842         var  ret = new Roo.factory(cfg);
38843         return ret;
38844         
38845         
38846         // add form..
38847         if (cfg.xtype.match(/^Form$/)) {
38848             
38849             var el;
38850             //if (this.footer) {
38851             //    el = this.footer.container.insertSibling(false, 'before');
38852             //} else {
38853                 el = this.el.createChild();
38854             //}
38855
38856             this.form = new  Roo.form.Form(cfg);
38857             
38858             
38859             if ( this.form.allItems.length) {
38860                 this.form.render(el.dom);
38861             }
38862             return this.form;
38863         }
38864         // should only have one of theses..
38865         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38866             // views.. should not be just added - used named prop 'view''
38867             
38868             cfg.el = this.el.appendChild(document.createElement("div"));
38869             // factory?
38870             
38871             var ret = new Roo.factory(cfg);
38872              
38873              ret.render && ret.render(false, ''); // render blank..
38874             this.view = ret;
38875             return ret;
38876         }
38877         return false;
38878     }
38879     \*/
38880 });
38881  
38882 /**
38883  * @class Roo.bootstrap.panel.Grid
38884  * @extends Roo.bootstrap.panel.Content
38885  * @constructor
38886  * Create a new GridPanel.
38887  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38888  * @param {Object} config A the config object
38889   
38890  */
38891
38892
38893
38894 Roo.bootstrap.panel.Grid = function(config)
38895 {
38896     
38897       
38898     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38899         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38900
38901     config.el = this.wrapper;
38902     //this.el = this.wrapper;
38903     
38904       if (config.container) {
38905         // ctor'ed from a Border/panel.grid
38906         
38907         
38908         this.wrapper.setStyle("overflow", "hidden");
38909         this.wrapper.addClass('roo-grid-container');
38910
38911     }
38912     
38913     
38914     if(config.toolbar){
38915         var tool_el = this.wrapper.createChild();    
38916         this.toolbar = Roo.factory(config.toolbar);
38917         var ti = [];
38918         if (config.toolbar.items) {
38919             ti = config.toolbar.items ;
38920             delete config.toolbar.items ;
38921         }
38922         
38923         var nitems = [];
38924         this.toolbar.render(tool_el);
38925         for(var i =0;i < ti.length;i++) {
38926           //  Roo.log(['add child', items[i]]);
38927             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38928         }
38929         this.toolbar.items = nitems;
38930         
38931         delete config.toolbar;
38932     }
38933     
38934     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38935     config.grid.scrollBody = true;;
38936     config.grid.monitorWindowResize = false; // turn off autosizing
38937     config.grid.autoHeight = false;
38938     config.grid.autoWidth = false;
38939     
38940     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38941     
38942     if (config.background) {
38943         // render grid on panel activation (if panel background)
38944         this.on('activate', function(gp) {
38945             if (!gp.grid.rendered) {
38946                 gp.grid.render(this.wrapper);
38947                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38948             }
38949         });
38950             
38951     } else {
38952         this.grid.render(this.wrapper);
38953         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38954
38955     }
38956     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38957     // ??? needed ??? config.el = this.wrapper;
38958     
38959     
38960     
38961   
38962     // xtype created footer. - not sure if will work as we normally have to render first..
38963     if (this.footer && !this.footer.el && this.footer.xtype) {
38964         
38965         var ctr = this.grid.getView().getFooterPanel(true);
38966         this.footer.dataSource = this.grid.dataSource;
38967         this.footer = Roo.factory(this.footer, Roo);
38968         this.footer.render(ctr);
38969         
38970     }
38971     
38972     
38973     
38974     
38975      
38976 };
38977
38978 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38979     getId : function(){
38980         return this.grid.id;
38981     },
38982     
38983     /**
38984      * Returns the grid for this panel
38985      * @return {Roo.bootstrap.Table} 
38986      */
38987     getGrid : function(){
38988         return this.grid;    
38989     },
38990     
38991     setSize : function(width, height){
38992         if(!this.ignoreResize(width, height)){
38993             var grid = this.grid;
38994             var size = this.adjustForComponents(width, height);
38995             var gridel = grid.getGridEl();
38996             gridel.setSize(size.width, size.height);
38997             /*
38998             var thd = grid.getGridEl().select('thead',true).first();
38999             var tbd = grid.getGridEl().select('tbody', true).first();
39000             if (tbd) {
39001                 tbd.setSize(width, height - thd.getHeight());
39002             }
39003             */
39004             grid.autoSize();
39005         }
39006     },
39007      
39008     
39009     
39010     beforeSlide : function(){
39011         this.grid.getView().scroller.clip();
39012     },
39013     
39014     afterSlide : function(){
39015         this.grid.getView().scroller.unclip();
39016     },
39017     
39018     destroy : function(){
39019         this.grid.destroy();
39020         delete this.grid;
39021         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39022     }
39023 });
39024
39025 /**
39026  * @class Roo.bootstrap.panel.Nest
39027  * @extends Roo.bootstrap.panel.Content
39028  * @constructor
39029  * Create a new Panel, that can contain a layout.Border.
39030  * 
39031  * 
39032  * @param {Roo.BorderLayout} layout The layout for this panel
39033  * @param {String/Object} config A string to set only the title or a config object
39034  */
39035 Roo.bootstrap.panel.Nest = function(config)
39036 {
39037     // construct with only one argument..
39038     /* FIXME - implement nicer consturctors
39039     if (layout.layout) {
39040         config = layout;
39041         layout = config.layout;
39042         delete config.layout;
39043     }
39044     if (layout.xtype && !layout.getEl) {
39045         // then layout needs constructing..
39046         layout = Roo.factory(layout, Roo);
39047     }
39048     */
39049     
39050     config.el =  config.layout.getEl();
39051     
39052     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39053     
39054     config.layout.monitorWindowResize = false; // turn off autosizing
39055     this.layout = config.layout;
39056     this.layout.getEl().addClass("roo-layout-nested-layout");
39057     this.layout.parent = this;
39058     
39059     
39060     
39061     
39062 };
39063
39064 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39065
39066     setSize : function(width, height){
39067         if(!this.ignoreResize(width, height)){
39068             var size = this.adjustForComponents(width, height);
39069             var el = this.layout.getEl();
39070             if (size.height < 1) {
39071                 el.setWidth(size.width);   
39072             } else {
39073                 el.setSize(size.width, size.height);
39074             }
39075             var touch = el.dom.offsetWidth;
39076             this.layout.layout();
39077             // ie requires a double layout on the first pass
39078             if(Roo.isIE && !this.initialized){
39079                 this.initialized = true;
39080                 this.layout.layout();
39081             }
39082         }
39083     },
39084     
39085     // activate all subpanels if not currently active..
39086     
39087     setActiveState : function(active){
39088         this.active = active;
39089         this.setActiveClass(active);
39090         
39091         if(!active){
39092             this.fireEvent("deactivate", this);
39093             return;
39094         }
39095         
39096         this.fireEvent("activate", this);
39097         // not sure if this should happen before or after..
39098         if (!this.layout) {
39099             return; // should not happen..
39100         }
39101         var reg = false;
39102         for (var r in this.layout.regions) {
39103             reg = this.layout.getRegion(r);
39104             if (reg.getActivePanel()) {
39105                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39106                 reg.setActivePanel(reg.getActivePanel());
39107                 continue;
39108             }
39109             if (!reg.panels.length) {
39110                 continue;
39111             }
39112             reg.showPanel(reg.getPanel(0));
39113         }
39114         
39115         
39116         
39117         
39118     },
39119     
39120     /**
39121      * Returns the nested BorderLayout for this panel
39122      * @return {Roo.BorderLayout} 
39123      */
39124     getLayout : function(){
39125         return this.layout;
39126     },
39127     
39128      /**
39129      * Adds a xtype elements to the layout of the nested panel
39130      * <pre><code>
39131
39132 panel.addxtype({
39133        xtype : 'ContentPanel',
39134        region: 'west',
39135        items: [ .... ]
39136    }
39137 );
39138
39139 panel.addxtype({
39140         xtype : 'NestedLayoutPanel',
39141         region: 'west',
39142         layout: {
39143            center: { },
39144            west: { }   
39145         },
39146         items : [ ... list of content panels or nested layout panels.. ]
39147    }
39148 );
39149 </code></pre>
39150      * @param {Object} cfg Xtype definition of item to add.
39151      */
39152     addxtype : function(cfg) {
39153         return this.layout.addxtype(cfg);
39154     
39155     }
39156 });/*
39157  * Based on:
39158  * Ext JS Library 1.1.1
39159  * Copyright(c) 2006-2007, Ext JS, LLC.
39160  *
39161  * Originally Released Under LGPL - original licence link has changed is not relivant.
39162  *
39163  * Fork - LGPL
39164  * <script type="text/javascript">
39165  */
39166 /**
39167  * @class Roo.TabPanel
39168  * @extends Roo.util.Observable
39169  * A lightweight tab container.
39170  * <br><br>
39171  * Usage:
39172  * <pre><code>
39173 // basic tabs 1, built from existing content
39174 var tabs = new Roo.TabPanel("tabs1");
39175 tabs.addTab("script", "View Script");
39176 tabs.addTab("markup", "View Markup");
39177 tabs.activate("script");
39178
39179 // more advanced tabs, built from javascript
39180 var jtabs = new Roo.TabPanel("jtabs");
39181 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39182
39183 // set up the UpdateManager
39184 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39185 var updater = tab2.getUpdateManager();
39186 updater.setDefaultUrl("ajax1.htm");
39187 tab2.on('activate', updater.refresh, updater, true);
39188
39189 // Use setUrl for Ajax loading
39190 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39191 tab3.setUrl("ajax2.htm", null, true);
39192
39193 // Disabled tab
39194 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39195 tab4.disable();
39196
39197 jtabs.activate("jtabs-1");
39198  * </code></pre>
39199  * @constructor
39200  * Create a new TabPanel.
39201  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39202  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39203  */
39204 Roo.bootstrap.panel.Tabs = function(config){
39205     /**
39206     * The container element for this TabPanel.
39207     * @type Roo.Element
39208     */
39209     this.el = Roo.get(config.el);
39210     delete config.el;
39211     if(config){
39212         if(typeof config == "boolean"){
39213             this.tabPosition = config ? "bottom" : "top";
39214         }else{
39215             Roo.apply(this, config);
39216         }
39217     }
39218     
39219     if(this.tabPosition == "bottom"){
39220         // if tabs are at the bottom = create the body first.
39221         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39222         this.el.addClass("roo-tabs-bottom");
39223     }
39224     // next create the tabs holders
39225     
39226     if (this.tabPosition == "west"){
39227         
39228         var reg = this.region; // fake it..
39229         while (reg) {
39230             if (!reg.mgr.parent) {
39231                 break;
39232             }
39233             reg = reg.mgr.parent.region;
39234         }
39235         Roo.log("got nest?");
39236         Roo.log(reg);
39237         if (reg.mgr.getRegion('west')) {
39238             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39239             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39240             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39241             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39242             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39243         
39244             
39245         }
39246         
39247         
39248     } else {
39249      
39250         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39251         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39252         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39253         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39254     }
39255     
39256     
39257     if(Roo.isIE){
39258         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39259     }
39260     
39261     // finally - if tabs are at the top, then create the body last..
39262     if(this.tabPosition != "bottom"){
39263         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39264          * @type Roo.Element
39265          */
39266         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39267         this.el.addClass("roo-tabs-top");
39268     }
39269     this.items = [];
39270
39271     this.bodyEl.setStyle("position", "relative");
39272
39273     this.active = null;
39274     this.activateDelegate = this.activate.createDelegate(this);
39275
39276     this.addEvents({
39277         /**
39278          * @event tabchange
39279          * Fires when the active tab changes
39280          * @param {Roo.TabPanel} this
39281          * @param {Roo.TabPanelItem} activePanel The new active tab
39282          */
39283         "tabchange": true,
39284         /**
39285          * @event beforetabchange
39286          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39287          * @param {Roo.TabPanel} this
39288          * @param {Object} e Set cancel to true on this object to cancel the tab change
39289          * @param {Roo.TabPanelItem} tab The tab being changed to
39290          */
39291         "beforetabchange" : true
39292     });
39293
39294     Roo.EventManager.onWindowResize(this.onResize, this);
39295     this.cpad = this.el.getPadding("lr");
39296     this.hiddenCount = 0;
39297
39298
39299     // toolbar on the tabbar support...
39300     if (this.toolbar) {
39301         alert("no toolbar support yet");
39302         this.toolbar  = false;
39303         /*
39304         var tcfg = this.toolbar;
39305         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39306         this.toolbar = new Roo.Toolbar(tcfg);
39307         if (Roo.isSafari) {
39308             var tbl = tcfg.container.child('table', true);
39309             tbl.setAttribute('width', '100%');
39310         }
39311         */
39312         
39313     }
39314    
39315
39316
39317     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39318 };
39319
39320 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39321     /*
39322      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39323      */
39324     tabPosition : "top",
39325     /*
39326      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39327      */
39328     currentTabWidth : 0,
39329     /*
39330      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39331      */
39332     minTabWidth : 40,
39333     /*
39334      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39335      */
39336     maxTabWidth : 250,
39337     /*
39338      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39339      */
39340     preferredTabWidth : 175,
39341     /*
39342      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39343      */
39344     resizeTabs : false,
39345     /*
39346      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39347      */
39348     monitorResize : true,
39349     /*
39350      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39351      */
39352     toolbar : false,  // set by caller..
39353     
39354     region : false, /// set by caller
39355     
39356     disableTooltips : true, // not used yet...
39357
39358     /**
39359      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39360      * @param {String} id The id of the div to use <b>or create</b>
39361      * @param {String} text The text for the tab
39362      * @param {String} content (optional) Content to put in the TabPanelItem body
39363      * @param {Boolean} closable (optional) True to create a close icon on the tab
39364      * @return {Roo.TabPanelItem} The created TabPanelItem
39365      */
39366     addTab : function(id, text, content, closable, tpl)
39367     {
39368         var item = new Roo.bootstrap.panel.TabItem({
39369             panel: this,
39370             id : id,
39371             text : text,
39372             closable : closable,
39373             tpl : tpl
39374         });
39375         this.addTabItem(item);
39376         if(content){
39377             item.setContent(content);
39378         }
39379         return item;
39380     },
39381
39382     /**
39383      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39384      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39385      * @return {Roo.TabPanelItem}
39386      */
39387     getTab : function(id){
39388         return this.items[id];
39389     },
39390
39391     /**
39392      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39393      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39394      */
39395     hideTab : function(id){
39396         var t = this.items[id];
39397         if(!t.isHidden()){
39398            t.setHidden(true);
39399            this.hiddenCount++;
39400            this.autoSizeTabs();
39401         }
39402     },
39403
39404     /**
39405      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39406      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39407      */
39408     unhideTab : function(id){
39409         var t = this.items[id];
39410         if(t.isHidden()){
39411            t.setHidden(false);
39412            this.hiddenCount--;
39413            this.autoSizeTabs();
39414         }
39415     },
39416
39417     /**
39418      * Adds an existing {@link Roo.TabPanelItem}.
39419      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39420      */
39421     addTabItem : function(item)
39422     {
39423         this.items[item.id] = item;
39424         this.items.push(item);
39425         this.autoSizeTabs();
39426       //  if(this.resizeTabs){
39427     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39428   //         this.autoSizeTabs();
39429 //        }else{
39430 //            item.autoSize();
39431        // }
39432     },
39433
39434     /**
39435      * Removes a {@link Roo.TabPanelItem}.
39436      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39437      */
39438     removeTab : function(id){
39439         var items = this.items;
39440         var tab = items[id];
39441         if(!tab) { return; }
39442         var index = items.indexOf(tab);
39443         if(this.active == tab && items.length > 1){
39444             var newTab = this.getNextAvailable(index);
39445             if(newTab) {
39446                 newTab.activate();
39447             }
39448         }
39449         this.stripEl.dom.removeChild(tab.pnode.dom);
39450         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39451             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39452         }
39453         items.splice(index, 1);
39454         delete this.items[tab.id];
39455         tab.fireEvent("close", tab);
39456         tab.purgeListeners();
39457         this.autoSizeTabs();
39458     },
39459
39460     getNextAvailable : function(start){
39461         var items = this.items;
39462         var index = start;
39463         // look for a next tab that will slide over to
39464         // replace the one being removed
39465         while(index < items.length){
39466             var item = items[++index];
39467             if(item && !item.isHidden()){
39468                 return item;
39469             }
39470         }
39471         // if one isn't found select the previous tab (on the left)
39472         index = start;
39473         while(index >= 0){
39474             var item = items[--index];
39475             if(item && !item.isHidden()){
39476                 return item;
39477             }
39478         }
39479         return null;
39480     },
39481
39482     /**
39483      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39484      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39485      */
39486     disableTab : function(id){
39487         var tab = this.items[id];
39488         if(tab && this.active != tab){
39489             tab.disable();
39490         }
39491     },
39492
39493     /**
39494      * Enables a {@link Roo.TabPanelItem} that is disabled.
39495      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39496      */
39497     enableTab : function(id){
39498         var tab = this.items[id];
39499         tab.enable();
39500     },
39501
39502     /**
39503      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39504      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39505      * @return {Roo.TabPanelItem} The TabPanelItem.
39506      */
39507     activate : function(id)
39508     {
39509         //Roo.log('activite:'  + id);
39510         
39511         var tab = this.items[id];
39512         if(!tab){
39513             return null;
39514         }
39515         if(tab == this.active || tab.disabled){
39516             return tab;
39517         }
39518         var e = {};
39519         this.fireEvent("beforetabchange", this, e, tab);
39520         if(e.cancel !== true && !tab.disabled){
39521             if(this.active){
39522                 this.active.hide();
39523             }
39524             this.active = this.items[id];
39525             this.active.show();
39526             this.fireEvent("tabchange", this, this.active);
39527         }
39528         return tab;
39529     },
39530
39531     /**
39532      * Gets the active {@link Roo.TabPanelItem}.
39533      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39534      */
39535     getActiveTab : function(){
39536         return this.active;
39537     },
39538
39539     /**
39540      * Updates the tab body element to fit the height of the container element
39541      * for overflow scrolling
39542      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39543      */
39544     syncHeight : function(targetHeight){
39545         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39546         var bm = this.bodyEl.getMargins();
39547         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39548         this.bodyEl.setHeight(newHeight);
39549         return newHeight;
39550     },
39551
39552     onResize : function(){
39553         if(this.monitorResize){
39554             this.autoSizeTabs();
39555         }
39556     },
39557
39558     /**
39559      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39560      */
39561     beginUpdate : function(){
39562         this.updating = true;
39563     },
39564
39565     /**
39566      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39567      */
39568     endUpdate : function(){
39569         this.updating = false;
39570         this.autoSizeTabs();
39571     },
39572
39573     /**
39574      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39575      */
39576     autoSizeTabs : function()
39577     {
39578         var count = this.items.length;
39579         var vcount = count - this.hiddenCount;
39580         
39581         if (vcount < 2) {
39582             this.stripEl.hide();
39583         } else {
39584             this.stripEl.show();
39585         }
39586         
39587         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39588             return;
39589         }
39590         
39591         
39592         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39593         var availWidth = Math.floor(w / vcount);
39594         var b = this.stripBody;
39595         if(b.getWidth() > w){
39596             var tabs = this.items;
39597             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39598             if(availWidth < this.minTabWidth){
39599                 /*if(!this.sleft){    // incomplete scrolling code
39600                     this.createScrollButtons();
39601                 }
39602                 this.showScroll();
39603                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39604             }
39605         }else{
39606             if(this.currentTabWidth < this.preferredTabWidth){
39607                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39608             }
39609         }
39610     },
39611
39612     /**
39613      * Returns the number of tabs in this TabPanel.
39614      * @return {Number}
39615      */
39616      getCount : function(){
39617          return this.items.length;
39618      },
39619
39620     /**
39621      * Resizes all the tabs to the passed width
39622      * @param {Number} The new width
39623      */
39624     setTabWidth : function(width){
39625         this.currentTabWidth = width;
39626         for(var i = 0, len = this.items.length; i < len; i++) {
39627                 if(!this.items[i].isHidden()) {
39628                 this.items[i].setWidth(width);
39629             }
39630         }
39631     },
39632
39633     /**
39634      * Destroys this TabPanel
39635      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39636      */
39637     destroy : function(removeEl){
39638         Roo.EventManager.removeResizeListener(this.onResize, this);
39639         for(var i = 0, len = this.items.length; i < len; i++){
39640             this.items[i].purgeListeners();
39641         }
39642         if(removeEl === true){
39643             this.el.update("");
39644             this.el.remove();
39645         }
39646     },
39647     
39648     createStrip : function(container)
39649     {
39650         var strip = document.createElement("nav");
39651         strip.className = Roo.bootstrap.version == 4 ?
39652             "navbar-light bg-light" : 
39653             "navbar navbar-default"; //"x-tabs-wrap";
39654         container.appendChild(strip);
39655         return strip;
39656     },
39657     
39658     createStripList : function(strip)
39659     {
39660         // div wrapper for retard IE
39661         // returns the "tr" element.
39662         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39663         //'<div class="x-tabs-strip-wrap">'+
39664           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39665           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39666         return strip.firstChild; //.firstChild.firstChild.firstChild;
39667     },
39668     createBody : function(container)
39669     {
39670         var body = document.createElement("div");
39671         Roo.id(body, "tab-body");
39672         //Roo.fly(body).addClass("x-tabs-body");
39673         Roo.fly(body).addClass("tab-content");
39674         container.appendChild(body);
39675         return body;
39676     },
39677     createItemBody :function(bodyEl, id){
39678         var body = Roo.getDom(id);
39679         if(!body){
39680             body = document.createElement("div");
39681             body.id = id;
39682         }
39683         //Roo.fly(body).addClass("x-tabs-item-body");
39684         Roo.fly(body).addClass("tab-pane");
39685          bodyEl.insertBefore(body, bodyEl.firstChild);
39686         return body;
39687     },
39688     /** @private */
39689     createStripElements :  function(stripEl, text, closable, tpl)
39690     {
39691         var td = document.createElement("li"); // was td..
39692         td.className = 'nav-item';
39693         
39694         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39695         
39696         
39697         stripEl.appendChild(td);
39698         /*if(closable){
39699             td.className = "x-tabs-closable";
39700             if(!this.closeTpl){
39701                 this.closeTpl = new Roo.Template(
39702                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39703                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39704                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39705                 );
39706             }
39707             var el = this.closeTpl.overwrite(td, {"text": text});
39708             var close = el.getElementsByTagName("div")[0];
39709             var inner = el.getElementsByTagName("em")[0];
39710             return {"el": el, "close": close, "inner": inner};
39711         } else {
39712         */
39713         // not sure what this is..
39714 //            if(!this.tabTpl){
39715                 //this.tabTpl = new Roo.Template(
39716                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39717                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39718                 //);
39719 //                this.tabTpl = new Roo.Template(
39720 //                   '<a href="#">' +
39721 //                   '<span unselectable="on"' +
39722 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39723 //                            ' >{text}</span></a>'
39724 //                );
39725 //                
39726 //            }
39727
39728
39729             var template = tpl || this.tabTpl || false;
39730             
39731             if(!template){
39732                 template =  new Roo.Template(
39733                         Roo.bootstrap.version == 4 ? 
39734                             (
39735                                 '<a class="nav-link" href="#" unselectable="on"' +
39736                                      (this.disableTooltips ? '' : ' title="{text}"') +
39737                                      ' >{text}</a>'
39738                             ) : (
39739                                 '<a class="nav-link" href="#">' +
39740                                 '<span unselectable="on"' +
39741                                          (this.disableTooltips ? '' : ' title="{text}"') +
39742                                     ' >{text}</span></a>'
39743                             )
39744                 );
39745             }
39746             
39747             switch (typeof(template)) {
39748                 case 'object' :
39749                     break;
39750                 case 'string' :
39751                     template = new Roo.Template(template);
39752                     break;
39753                 default :
39754                     break;
39755             }
39756             
39757             var el = template.overwrite(td, {"text": text});
39758             
39759             var inner = el.getElementsByTagName("span")[0];
39760             
39761             return {"el": el, "inner": inner};
39762             
39763     }
39764         
39765     
39766 });
39767
39768 /**
39769  * @class Roo.TabPanelItem
39770  * @extends Roo.util.Observable
39771  * Represents an individual item (tab plus body) in a TabPanel.
39772  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39773  * @param {String} id The id of this TabPanelItem
39774  * @param {String} text The text for the tab of this TabPanelItem
39775  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39776  */
39777 Roo.bootstrap.panel.TabItem = function(config){
39778     /**
39779      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39780      * @type Roo.TabPanel
39781      */
39782     this.tabPanel = config.panel;
39783     /**
39784      * The id for this TabPanelItem
39785      * @type String
39786      */
39787     this.id = config.id;
39788     /** @private */
39789     this.disabled = false;
39790     /** @private */
39791     this.text = config.text;
39792     /** @private */
39793     this.loaded = false;
39794     this.closable = config.closable;
39795
39796     /**
39797      * The body element for this TabPanelItem.
39798      * @type Roo.Element
39799      */
39800     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39801     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39802     this.bodyEl.setStyle("display", "block");
39803     this.bodyEl.setStyle("zoom", "1");
39804     //this.hideAction();
39805
39806     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39807     /** @private */
39808     this.el = Roo.get(els.el);
39809     this.inner = Roo.get(els.inner, true);
39810      this.textEl = Roo.bootstrap.version == 4 ?
39811         this.el : Roo.get(this.el.dom.firstChild, true);
39812
39813     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39814     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39815
39816     
39817 //    this.el.on("mousedown", this.onTabMouseDown, this);
39818     this.el.on("click", this.onTabClick, this);
39819     /** @private */
39820     if(config.closable){
39821         var c = Roo.get(els.close, true);
39822         c.dom.title = this.closeText;
39823         c.addClassOnOver("close-over");
39824         c.on("click", this.closeClick, this);
39825      }
39826
39827     this.addEvents({
39828          /**
39829          * @event activate
39830          * Fires when this tab becomes the active tab.
39831          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39832          * @param {Roo.TabPanelItem} this
39833          */
39834         "activate": true,
39835         /**
39836          * @event beforeclose
39837          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39838          * @param {Roo.TabPanelItem} this
39839          * @param {Object} e Set cancel to true on this object to cancel the close.
39840          */
39841         "beforeclose": true,
39842         /**
39843          * @event close
39844          * Fires when this tab is closed.
39845          * @param {Roo.TabPanelItem} this
39846          */
39847          "close": true,
39848         /**
39849          * @event deactivate
39850          * Fires when this tab is no longer the active tab.
39851          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39852          * @param {Roo.TabPanelItem} this
39853          */
39854          "deactivate" : true
39855     });
39856     this.hidden = false;
39857
39858     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39859 };
39860
39861 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39862            {
39863     purgeListeners : function(){
39864        Roo.util.Observable.prototype.purgeListeners.call(this);
39865        this.el.removeAllListeners();
39866     },
39867     /**
39868      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39869      */
39870     show : function(){
39871         this.status_node.addClass("active");
39872         this.showAction();
39873         if(Roo.isOpera){
39874             this.tabPanel.stripWrap.repaint();
39875         }
39876         this.fireEvent("activate", this.tabPanel, this);
39877     },
39878
39879     /**
39880      * Returns true if this tab is the active tab.
39881      * @return {Boolean}
39882      */
39883     isActive : function(){
39884         return this.tabPanel.getActiveTab() == this;
39885     },
39886
39887     /**
39888      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39889      */
39890     hide : function(){
39891         this.status_node.removeClass("active");
39892         this.hideAction();
39893         this.fireEvent("deactivate", this.tabPanel, this);
39894     },
39895
39896     hideAction : function(){
39897         this.bodyEl.hide();
39898         this.bodyEl.setStyle("position", "absolute");
39899         this.bodyEl.setLeft("-20000px");
39900         this.bodyEl.setTop("-20000px");
39901     },
39902
39903     showAction : function(){
39904         this.bodyEl.setStyle("position", "relative");
39905         this.bodyEl.setTop("");
39906         this.bodyEl.setLeft("");
39907         this.bodyEl.show();
39908     },
39909
39910     /**
39911      * Set the tooltip for the tab.
39912      * @param {String} tooltip The tab's tooltip
39913      */
39914     setTooltip : function(text){
39915         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39916             this.textEl.dom.qtip = text;
39917             this.textEl.dom.removeAttribute('title');
39918         }else{
39919             this.textEl.dom.title = text;
39920         }
39921     },
39922
39923     onTabClick : function(e){
39924         e.preventDefault();
39925         this.tabPanel.activate(this.id);
39926     },
39927
39928     onTabMouseDown : function(e){
39929         e.preventDefault();
39930         this.tabPanel.activate(this.id);
39931     },
39932 /*
39933     getWidth : function(){
39934         return this.inner.getWidth();
39935     },
39936
39937     setWidth : function(width){
39938         var iwidth = width - this.linode.getPadding("lr");
39939         this.inner.setWidth(iwidth);
39940         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39941         this.linode.setWidth(width);
39942     },
39943 */
39944     /**
39945      * Show or hide the tab
39946      * @param {Boolean} hidden True to hide or false to show.
39947      */
39948     setHidden : function(hidden){
39949         this.hidden = hidden;
39950         this.linode.setStyle("display", hidden ? "none" : "");
39951     },
39952
39953     /**
39954      * Returns true if this tab is "hidden"
39955      * @return {Boolean}
39956      */
39957     isHidden : function(){
39958         return this.hidden;
39959     },
39960
39961     /**
39962      * Returns the text for this tab
39963      * @return {String}
39964      */
39965     getText : function(){
39966         return this.text;
39967     },
39968     /*
39969     autoSize : function(){
39970         //this.el.beginMeasure();
39971         this.textEl.setWidth(1);
39972         /*
39973          *  #2804 [new] Tabs in Roojs
39974          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39975          */
39976         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39977         //this.el.endMeasure();
39978     //},
39979
39980     /**
39981      * Sets the text for the tab (Note: this also sets the tooltip text)
39982      * @param {String} text The tab's text and tooltip
39983      */
39984     setText : function(text){
39985         this.text = text;
39986         this.textEl.update(text);
39987         this.setTooltip(text);
39988         //if(!this.tabPanel.resizeTabs){
39989         //    this.autoSize();
39990         //}
39991     },
39992     /**
39993      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39994      */
39995     activate : function(){
39996         this.tabPanel.activate(this.id);
39997     },
39998
39999     /**
40000      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40001      */
40002     disable : function(){
40003         if(this.tabPanel.active != this){
40004             this.disabled = true;
40005             this.status_node.addClass("disabled");
40006         }
40007     },
40008
40009     /**
40010      * Enables this TabPanelItem if it was previously disabled.
40011      */
40012     enable : function(){
40013         this.disabled = false;
40014         this.status_node.removeClass("disabled");
40015     },
40016
40017     /**
40018      * Sets the content for this TabPanelItem.
40019      * @param {String} content The content
40020      * @param {Boolean} loadScripts true to look for and load scripts
40021      */
40022     setContent : function(content, loadScripts){
40023         this.bodyEl.update(content, loadScripts);
40024     },
40025
40026     /**
40027      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40028      * @return {Roo.UpdateManager} The UpdateManager
40029      */
40030     getUpdateManager : function(){
40031         return this.bodyEl.getUpdateManager();
40032     },
40033
40034     /**
40035      * Set a URL to be used to load the content for this TabPanelItem.
40036      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40037      * @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)
40038      * @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)
40039      * @return {Roo.UpdateManager} The UpdateManager
40040      */
40041     setUrl : function(url, params, loadOnce){
40042         if(this.refreshDelegate){
40043             this.un('activate', this.refreshDelegate);
40044         }
40045         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40046         this.on("activate", this.refreshDelegate);
40047         return this.bodyEl.getUpdateManager();
40048     },
40049
40050     /** @private */
40051     _handleRefresh : function(url, params, loadOnce){
40052         if(!loadOnce || !this.loaded){
40053             var updater = this.bodyEl.getUpdateManager();
40054             updater.update(url, params, this._setLoaded.createDelegate(this));
40055         }
40056     },
40057
40058     /**
40059      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40060      *   Will fail silently if the setUrl method has not been called.
40061      *   This does not activate the panel, just updates its content.
40062      */
40063     refresh : function(){
40064         if(this.refreshDelegate){
40065            this.loaded = false;
40066            this.refreshDelegate();
40067         }
40068     },
40069
40070     /** @private */
40071     _setLoaded : function(){
40072         this.loaded = true;
40073     },
40074
40075     /** @private */
40076     closeClick : function(e){
40077         var o = {};
40078         e.stopEvent();
40079         this.fireEvent("beforeclose", this, o);
40080         if(o.cancel !== true){
40081             this.tabPanel.removeTab(this.id);
40082         }
40083     },
40084     /**
40085      * The text displayed in the tooltip for the close icon.
40086      * @type String
40087      */
40088     closeText : "Close this tab"
40089 });
40090 /**
40091 *    This script refer to:
40092 *    Title: International Telephone Input
40093 *    Author: Jack O'Connor
40094 *    Code version:  v12.1.12
40095 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40096 **/
40097
40098 Roo.bootstrap.PhoneInputData = function() {
40099     var d = [
40100       [
40101         "Afghanistan (‫افغانستان‬‎)",
40102         "af",
40103         "93"
40104       ],
40105       [
40106         "Albania (Shqipëri)",
40107         "al",
40108         "355"
40109       ],
40110       [
40111         "Algeria (‫الجزائر‬‎)",
40112         "dz",
40113         "213"
40114       ],
40115       [
40116         "American Samoa",
40117         "as",
40118         "1684"
40119       ],
40120       [
40121         "Andorra",
40122         "ad",
40123         "376"
40124       ],
40125       [
40126         "Angola",
40127         "ao",
40128         "244"
40129       ],
40130       [
40131         "Anguilla",
40132         "ai",
40133         "1264"
40134       ],
40135       [
40136         "Antigua and Barbuda",
40137         "ag",
40138         "1268"
40139       ],
40140       [
40141         "Argentina",
40142         "ar",
40143         "54"
40144       ],
40145       [
40146         "Armenia (Հայաստան)",
40147         "am",
40148         "374"
40149       ],
40150       [
40151         "Aruba",
40152         "aw",
40153         "297"
40154       ],
40155       [
40156         "Australia",
40157         "au",
40158         "61",
40159         0
40160       ],
40161       [
40162         "Austria (Österreich)",
40163         "at",
40164         "43"
40165       ],
40166       [
40167         "Azerbaijan (Azərbaycan)",
40168         "az",
40169         "994"
40170       ],
40171       [
40172         "Bahamas",
40173         "bs",
40174         "1242"
40175       ],
40176       [
40177         "Bahrain (‫البحرين‬‎)",
40178         "bh",
40179         "973"
40180       ],
40181       [
40182         "Bangladesh (বাংলাদেশ)",
40183         "bd",
40184         "880"
40185       ],
40186       [
40187         "Barbados",
40188         "bb",
40189         "1246"
40190       ],
40191       [
40192         "Belarus (Беларусь)",
40193         "by",
40194         "375"
40195       ],
40196       [
40197         "Belgium (België)",
40198         "be",
40199         "32"
40200       ],
40201       [
40202         "Belize",
40203         "bz",
40204         "501"
40205       ],
40206       [
40207         "Benin (Bénin)",
40208         "bj",
40209         "229"
40210       ],
40211       [
40212         "Bermuda",
40213         "bm",
40214         "1441"
40215       ],
40216       [
40217         "Bhutan (འབྲུག)",
40218         "bt",
40219         "975"
40220       ],
40221       [
40222         "Bolivia",
40223         "bo",
40224         "591"
40225       ],
40226       [
40227         "Bosnia and Herzegovina (Босна и Херцеговина)",
40228         "ba",
40229         "387"
40230       ],
40231       [
40232         "Botswana",
40233         "bw",
40234         "267"
40235       ],
40236       [
40237         "Brazil (Brasil)",
40238         "br",
40239         "55"
40240       ],
40241       [
40242         "British Indian Ocean Territory",
40243         "io",
40244         "246"
40245       ],
40246       [
40247         "British Virgin Islands",
40248         "vg",
40249         "1284"
40250       ],
40251       [
40252         "Brunei",
40253         "bn",
40254         "673"
40255       ],
40256       [
40257         "Bulgaria (България)",
40258         "bg",
40259         "359"
40260       ],
40261       [
40262         "Burkina Faso",
40263         "bf",
40264         "226"
40265       ],
40266       [
40267         "Burundi (Uburundi)",
40268         "bi",
40269         "257"
40270       ],
40271       [
40272         "Cambodia (កម្ពុជា)",
40273         "kh",
40274         "855"
40275       ],
40276       [
40277         "Cameroon (Cameroun)",
40278         "cm",
40279         "237"
40280       ],
40281       [
40282         "Canada",
40283         "ca",
40284         "1",
40285         1,
40286         ["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"]
40287       ],
40288       [
40289         "Cape Verde (Kabu Verdi)",
40290         "cv",
40291         "238"
40292       ],
40293       [
40294         "Caribbean Netherlands",
40295         "bq",
40296         "599",
40297         1
40298       ],
40299       [
40300         "Cayman Islands",
40301         "ky",
40302         "1345"
40303       ],
40304       [
40305         "Central African Republic (République centrafricaine)",
40306         "cf",
40307         "236"
40308       ],
40309       [
40310         "Chad (Tchad)",
40311         "td",
40312         "235"
40313       ],
40314       [
40315         "Chile",
40316         "cl",
40317         "56"
40318       ],
40319       [
40320         "China (中国)",
40321         "cn",
40322         "86"
40323       ],
40324       [
40325         "Christmas Island",
40326         "cx",
40327         "61",
40328         2
40329       ],
40330       [
40331         "Cocos (Keeling) Islands",
40332         "cc",
40333         "61",
40334         1
40335       ],
40336       [
40337         "Colombia",
40338         "co",
40339         "57"
40340       ],
40341       [
40342         "Comoros (‫جزر القمر‬‎)",
40343         "km",
40344         "269"
40345       ],
40346       [
40347         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40348         "cd",
40349         "243"
40350       ],
40351       [
40352         "Congo (Republic) (Congo-Brazzaville)",
40353         "cg",
40354         "242"
40355       ],
40356       [
40357         "Cook Islands",
40358         "ck",
40359         "682"
40360       ],
40361       [
40362         "Costa Rica",
40363         "cr",
40364         "506"
40365       ],
40366       [
40367         "Côte d’Ivoire",
40368         "ci",
40369         "225"
40370       ],
40371       [
40372         "Croatia (Hrvatska)",
40373         "hr",
40374         "385"
40375       ],
40376       [
40377         "Cuba",
40378         "cu",
40379         "53"
40380       ],
40381       [
40382         "Curaçao",
40383         "cw",
40384         "599",
40385         0
40386       ],
40387       [
40388         "Cyprus (Κύπρος)",
40389         "cy",
40390         "357"
40391       ],
40392       [
40393         "Czech Republic (Česká republika)",
40394         "cz",
40395         "420"
40396       ],
40397       [
40398         "Denmark (Danmark)",
40399         "dk",
40400         "45"
40401       ],
40402       [
40403         "Djibouti",
40404         "dj",
40405         "253"
40406       ],
40407       [
40408         "Dominica",
40409         "dm",
40410         "1767"
40411       ],
40412       [
40413         "Dominican Republic (República Dominicana)",
40414         "do",
40415         "1",
40416         2,
40417         ["809", "829", "849"]
40418       ],
40419       [
40420         "Ecuador",
40421         "ec",
40422         "593"
40423       ],
40424       [
40425         "Egypt (‫مصر‬‎)",
40426         "eg",
40427         "20"
40428       ],
40429       [
40430         "El Salvador",
40431         "sv",
40432         "503"
40433       ],
40434       [
40435         "Equatorial Guinea (Guinea Ecuatorial)",
40436         "gq",
40437         "240"
40438       ],
40439       [
40440         "Eritrea",
40441         "er",
40442         "291"
40443       ],
40444       [
40445         "Estonia (Eesti)",
40446         "ee",
40447         "372"
40448       ],
40449       [
40450         "Ethiopia",
40451         "et",
40452         "251"
40453       ],
40454       [
40455         "Falkland Islands (Islas Malvinas)",
40456         "fk",
40457         "500"
40458       ],
40459       [
40460         "Faroe Islands (Føroyar)",
40461         "fo",
40462         "298"
40463       ],
40464       [
40465         "Fiji",
40466         "fj",
40467         "679"
40468       ],
40469       [
40470         "Finland (Suomi)",
40471         "fi",
40472         "358",
40473         0
40474       ],
40475       [
40476         "France",
40477         "fr",
40478         "33"
40479       ],
40480       [
40481         "French Guiana (Guyane française)",
40482         "gf",
40483         "594"
40484       ],
40485       [
40486         "French Polynesia (Polynésie française)",
40487         "pf",
40488         "689"
40489       ],
40490       [
40491         "Gabon",
40492         "ga",
40493         "241"
40494       ],
40495       [
40496         "Gambia",
40497         "gm",
40498         "220"
40499       ],
40500       [
40501         "Georgia (საქართველო)",
40502         "ge",
40503         "995"
40504       ],
40505       [
40506         "Germany (Deutschland)",
40507         "de",
40508         "49"
40509       ],
40510       [
40511         "Ghana (Gaana)",
40512         "gh",
40513         "233"
40514       ],
40515       [
40516         "Gibraltar",
40517         "gi",
40518         "350"
40519       ],
40520       [
40521         "Greece (Ελλάδα)",
40522         "gr",
40523         "30"
40524       ],
40525       [
40526         "Greenland (Kalaallit Nunaat)",
40527         "gl",
40528         "299"
40529       ],
40530       [
40531         "Grenada",
40532         "gd",
40533         "1473"
40534       ],
40535       [
40536         "Guadeloupe",
40537         "gp",
40538         "590",
40539         0
40540       ],
40541       [
40542         "Guam",
40543         "gu",
40544         "1671"
40545       ],
40546       [
40547         "Guatemala",
40548         "gt",
40549         "502"
40550       ],
40551       [
40552         "Guernsey",
40553         "gg",
40554         "44",
40555         1
40556       ],
40557       [
40558         "Guinea (Guinée)",
40559         "gn",
40560         "224"
40561       ],
40562       [
40563         "Guinea-Bissau (Guiné Bissau)",
40564         "gw",
40565         "245"
40566       ],
40567       [
40568         "Guyana",
40569         "gy",
40570         "592"
40571       ],
40572       [
40573         "Haiti",
40574         "ht",
40575         "509"
40576       ],
40577       [
40578         "Honduras",
40579         "hn",
40580         "504"
40581       ],
40582       [
40583         "Hong Kong (香港)",
40584         "hk",
40585         "852"
40586       ],
40587       [
40588         "Hungary (Magyarország)",
40589         "hu",
40590         "36"
40591       ],
40592       [
40593         "Iceland (Ísland)",
40594         "is",
40595         "354"
40596       ],
40597       [
40598         "India (भारत)",
40599         "in",
40600         "91"
40601       ],
40602       [
40603         "Indonesia",
40604         "id",
40605         "62"
40606       ],
40607       [
40608         "Iran (‫ایران‬‎)",
40609         "ir",
40610         "98"
40611       ],
40612       [
40613         "Iraq (‫العراق‬‎)",
40614         "iq",
40615         "964"
40616       ],
40617       [
40618         "Ireland",
40619         "ie",
40620         "353"
40621       ],
40622       [
40623         "Isle of Man",
40624         "im",
40625         "44",
40626         2
40627       ],
40628       [
40629         "Israel (‫ישראל‬‎)",
40630         "il",
40631         "972"
40632       ],
40633       [
40634         "Italy (Italia)",
40635         "it",
40636         "39",
40637         0
40638       ],
40639       [
40640         "Jamaica",
40641         "jm",
40642         "1876"
40643       ],
40644       [
40645         "Japan (日本)",
40646         "jp",
40647         "81"
40648       ],
40649       [
40650         "Jersey",
40651         "je",
40652         "44",
40653         3
40654       ],
40655       [
40656         "Jordan (‫الأردن‬‎)",
40657         "jo",
40658         "962"
40659       ],
40660       [
40661         "Kazakhstan (Казахстан)",
40662         "kz",
40663         "7",
40664         1
40665       ],
40666       [
40667         "Kenya",
40668         "ke",
40669         "254"
40670       ],
40671       [
40672         "Kiribati",
40673         "ki",
40674         "686"
40675       ],
40676       [
40677         "Kosovo",
40678         "xk",
40679         "383"
40680       ],
40681       [
40682         "Kuwait (‫الكويت‬‎)",
40683         "kw",
40684         "965"
40685       ],
40686       [
40687         "Kyrgyzstan (Кыргызстан)",
40688         "kg",
40689         "996"
40690       ],
40691       [
40692         "Laos (ລາວ)",
40693         "la",
40694         "856"
40695       ],
40696       [
40697         "Latvia (Latvija)",
40698         "lv",
40699         "371"
40700       ],
40701       [
40702         "Lebanon (‫لبنان‬‎)",
40703         "lb",
40704         "961"
40705       ],
40706       [
40707         "Lesotho",
40708         "ls",
40709         "266"
40710       ],
40711       [
40712         "Liberia",
40713         "lr",
40714         "231"
40715       ],
40716       [
40717         "Libya (‫ليبيا‬‎)",
40718         "ly",
40719         "218"
40720       ],
40721       [
40722         "Liechtenstein",
40723         "li",
40724         "423"
40725       ],
40726       [
40727         "Lithuania (Lietuva)",
40728         "lt",
40729         "370"
40730       ],
40731       [
40732         "Luxembourg",
40733         "lu",
40734         "352"
40735       ],
40736       [
40737         "Macau (澳門)",
40738         "mo",
40739         "853"
40740       ],
40741       [
40742         "Macedonia (FYROM) (Македонија)",
40743         "mk",
40744         "389"
40745       ],
40746       [
40747         "Madagascar (Madagasikara)",
40748         "mg",
40749         "261"
40750       ],
40751       [
40752         "Malawi",
40753         "mw",
40754         "265"
40755       ],
40756       [
40757         "Malaysia",
40758         "my",
40759         "60"
40760       ],
40761       [
40762         "Maldives",
40763         "mv",
40764         "960"
40765       ],
40766       [
40767         "Mali",
40768         "ml",
40769         "223"
40770       ],
40771       [
40772         "Malta",
40773         "mt",
40774         "356"
40775       ],
40776       [
40777         "Marshall Islands",
40778         "mh",
40779         "692"
40780       ],
40781       [
40782         "Martinique",
40783         "mq",
40784         "596"
40785       ],
40786       [
40787         "Mauritania (‫موريتانيا‬‎)",
40788         "mr",
40789         "222"
40790       ],
40791       [
40792         "Mauritius (Moris)",
40793         "mu",
40794         "230"
40795       ],
40796       [
40797         "Mayotte",
40798         "yt",
40799         "262",
40800         1
40801       ],
40802       [
40803         "Mexico (México)",
40804         "mx",
40805         "52"
40806       ],
40807       [
40808         "Micronesia",
40809         "fm",
40810         "691"
40811       ],
40812       [
40813         "Moldova (Republica Moldova)",
40814         "md",
40815         "373"
40816       ],
40817       [
40818         "Monaco",
40819         "mc",
40820         "377"
40821       ],
40822       [
40823         "Mongolia (Монгол)",
40824         "mn",
40825         "976"
40826       ],
40827       [
40828         "Montenegro (Crna Gora)",
40829         "me",
40830         "382"
40831       ],
40832       [
40833         "Montserrat",
40834         "ms",
40835         "1664"
40836       ],
40837       [
40838         "Morocco (‫المغرب‬‎)",
40839         "ma",
40840         "212",
40841         0
40842       ],
40843       [
40844         "Mozambique (Moçambique)",
40845         "mz",
40846         "258"
40847       ],
40848       [
40849         "Myanmar (Burma) (မြန်မာ)",
40850         "mm",
40851         "95"
40852       ],
40853       [
40854         "Namibia (Namibië)",
40855         "na",
40856         "264"
40857       ],
40858       [
40859         "Nauru",
40860         "nr",
40861         "674"
40862       ],
40863       [
40864         "Nepal (नेपाल)",
40865         "np",
40866         "977"
40867       ],
40868       [
40869         "Netherlands (Nederland)",
40870         "nl",
40871         "31"
40872       ],
40873       [
40874         "New Caledonia (Nouvelle-Calédonie)",
40875         "nc",
40876         "687"
40877       ],
40878       [
40879         "New Zealand",
40880         "nz",
40881         "64"
40882       ],
40883       [
40884         "Nicaragua",
40885         "ni",
40886         "505"
40887       ],
40888       [
40889         "Niger (Nijar)",
40890         "ne",
40891         "227"
40892       ],
40893       [
40894         "Nigeria",
40895         "ng",
40896         "234"
40897       ],
40898       [
40899         "Niue",
40900         "nu",
40901         "683"
40902       ],
40903       [
40904         "Norfolk Island",
40905         "nf",
40906         "672"
40907       ],
40908       [
40909         "North Korea (조선 민주주의 인민 공화국)",
40910         "kp",
40911         "850"
40912       ],
40913       [
40914         "Northern Mariana Islands",
40915         "mp",
40916         "1670"
40917       ],
40918       [
40919         "Norway (Norge)",
40920         "no",
40921         "47",
40922         0
40923       ],
40924       [
40925         "Oman (‫عُمان‬‎)",
40926         "om",
40927         "968"
40928       ],
40929       [
40930         "Pakistan (‫پاکستان‬‎)",
40931         "pk",
40932         "92"
40933       ],
40934       [
40935         "Palau",
40936         "pw",
40937         "680"
40938       ],
40939       [
40940         "Palestine (‫فلسطين‬‎)",
40941         "ps",
40942         "970"
40943       ],
40944       [
40945         "Panama (Panamá)",
40946         "pa",
40947         "507"
40948       ],
40949       [
40950         "Papua New Guinea",
40951         "pg",
40952         "675"
40953       ],
40954       [
40955         "Paraguay",
40956         "py",
40957         "595"
40958       ],
40959       [
40960         "Peru (Perú)",
40961         "pe",
40962         "51"
40963       ],
40964       [
40965         "Philippines",
40966         "ph",
40967         "63"
40968       ],
40969       [
40970         "Poland (Polska)",
40971         "pl",
40972         "48"
40973       ],
40974       [
40975         "Portugal",
40976         "pt",
40977         "351"
40978       ],
40979       [
40980         "Puerto Rico",
40981         "pr",
40982         "1",
40983         3,
40984         ["787", "939"]
40985       ],
40986       [
40987         "Qatar (‫قطر‬‎)",
40988         "qa",
40989         "974"
40990       ],
40991       [
40992         "Réunion (La Réunion)",
40993         "re",
40994         "262",
40995         0
40996       ],
40997       [
40998         "Romania (România)",
40999         "ro",
41000         "40"
41001       ],
41002       [
41003         "Russia (Россия)",
41004         "ru",
41005         "7",
41006         0
41007       ],
41008       [
41009         "Rwanda",
41010         "rw",
41011         "250"
41012       ],
41013       [
41014         "Saint Barthélemy",
41015         "bl",
41016         "590",
41017         1
41018       ],
41019       [
41020         "Saint Helena",
41021         "sh",
41022         "290"
41023       ],
41024       [
41025         "Saint Kitts and Nevis",
41026         "kn",
41027         "1869"
41028       ],
41029       [
41030         "Saint Lucia",
41031         "lc",
41032         "1758"
41033       ],
41034       [
41035         "Saint Martin (Saint-Martin (partie française))",
41036         "mf",
41037         "590",
41038         2
41039       ],
41040       [
41041         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41042         "pm",
41043         "508"
41044       ],
41045       [
41046         "Saint Vincent and the Grenadines",
41047         "vc",
41048         "1784"
41049       ],
41050       [
41051         "Samoa",
41052         "ws",
41053         "685"
41054       ],
41055       [
41056         "San Marino",
41057         "sm",
41058         "378"
41059       ],
41060       [
41061         "São Tomé and Príncipe (São Tomé e Príncipe)",
41062         "st",
41063         "239"
41064       ],
41065       [
41066         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41067         "sa",
41068         "966"
41069       ],
41070       [
41071         "Senegal (Sénégal)",
41072         "sn",
41073         "221"
41074       ],
41075       [
41076         "Serbia (Србија)",
41077         "rs",
41078         "381"
41079       ],
41080       [
41081         "Seychelles",
41082         "sc",
41083         "248"
41084       ],
41085       [
41086         "Sierra Leone",
41087         "sl",
41088         "232"
41089       ],
41090       [
41091         "Singapore",
41092         "sg",
41093         "65"
41094       ],
41095       [
41096         "Sint Maarten",
41097         "sx",
41098         "1721"
41099       ],
41100       [
41101         "Slovakia (Slovensko)",
41102         "sk",
41103         "421"
41104       ],
41105       [
41106         "Slovenia (Slovenija)",
41107         "si",
41108         "386"
41109       ],
41110       [
41111         "Solomon Islands",
41112         "sb",
41113         "677"
41114       ],
41115       [
41116         "Somalia (Soomaaliya)",
41117         "so",
41118         "252"
41119       ],
41120       [
41121         "South Africa",
41122         "za",
41123         "27"
41124       ],
41125       [
41126         "South Korea (대한민국)",
41127         "kr",
41128         "82"
41129       ],
41130       [
41131         "South Sudan (‫جنوب السودان‬‎)",
41132         "ss",
41133         "211"
41134       ],
41135       [
41136         "Spain (España)",
41137         "es",
41138         "34"
41139       ],
41140       [
41141         "Sri Lanka (ශ්‍රී ලංකාව)",
41142         "lk",
41143         "94"
41144       ],
41145       [
41146         "Sudan (‫السودان‬‎)",
41147         "sd",
41148         "249"
41149       ],
41150       [
41151         "Suriname",
41152         "sr",
41153         "597"
41154       ],
41155       [
41156         "Svalbard and Jan Mayen",
41157         "sj",
41158         "47",
41159         1
41160       ],
41161       [
41162         "Swaziland",
41163         "sz",
41164         "268"
41165       ],
41166       [
41167         "Sweden (Sverige)",
41168         "se",
41169         "46"
41170       ],
41171       [
41172         "Switzerland (Schweiz)",
41173         "ch",
41174         "41"
41175       ],
41176       [
41177         "Syria (‫سوريا‬‎)",
41178         "sy",
41179         "963"
41180       ],
41181       [
41182         "Taiwan (台灣)",
41183         "tw",
41184         "886"
41185       ],
41186       [
41187         "Tajikistan",
41188         "tj",
41189         "992"
41190       ],
41191       [
41192         "Tanzania",
41193         "tz",
41194         "255"
41195       ],
41196       [
41197         "Thailand (ไทย)",
41198         "th",
41199         "66"
41200       ],
41201       [
41202         "Timor-Leste",
41203         "tl",
41204         "670"
41205       ],
41206       [
41207         "Togo",
41208         "tg",
41209         "228"
41210       ],
41211       [
41212         "Tokelau",
41213         "tk",
41214         "690"
41215       ],
41216       [
41217         "Tonga",
41218         "to",
41219         "676"
41220       ],
41221       [
41222         "Trinidad and Tobago",
41223         "tt",
41224         "1868"
41225       ],
41226       [
41227         "Tunisia (‫تونس‬‎)",
41228         "tn",
41229         "216"
41230       ],
41231       [
41232         "Turkey (Türkiye)",
41233         "tr",
41234         "90"
41235       ],
41236       [
41237         "Turkmenistan",
41238         "tm",
41239         "993"
41240       ],
41241       [
41242         "Turks and Caicos Islands",
41243         "tc",
41244         "1649"
41245       ],
41246       [
41247         "Tuvalu",
41248         "tv",
41249         "688"
41250       ],
41251       [
41252         "U.S. Virgin Islands",
41253         "vi",
41254         "1340"
41255       ],
41256       [
41257         "Uganda",
41258         "ug",
41259         "256"
41260       ],
41261       [
41262         "Ukraine (Україна)",
41263         "ua",
41264         "380"
41265       ],
41266       [
41267         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41268         "ae",
41269         "971"
41270       ],
41271       [
41272         "United Kingdom",
41273         "gb",
41274         "44",
41275         0
41276       ],
41277       [
41278         "United States",
41279         "us",
41280         "1",
41281         0
41282       ],
41283       [
41284         "Uruguay",
41285         "uy",
41286         "598"
41287       ],
41288       [
41289         "Uzbekistan (Oʻzbekiston)",
41290         "uz",
41291         "998"
41292       ],
41293       [
41294         "Vanuatu",
41295         "vu",
41296         "678"
41297       ],
41298       [
41299         "Vatican City (Città del Vaticano)",
41300         "va",
41301         "39",
41302         1
41303       ],
41304       [
41305         "Venezuela",
41306         "ve",
41307         "58"
41308       ],
41309       [
41310         "Vietnam (Việt Nam)",
41311         "vn",
41312         "84"
41313       ],
41314       [
41315         "Wallis and Futuna (Wallis-et-Futuna)",
41316         "wf",
41317         "681"
41318       ],
41319       [
41320         "Western Sahara (‫الصحراء الغربية‬‎)",
41321         "eh",
41322         "212",
41323         1
41324       ],
41325       [
41326         "Yemen (‫اليمن‬‎)",
41327         "ye",
41328         "967"
41329       ],
41330       [
41331         "Zambia",
41332         "zm",
41333         "260"
41334       ],
41335       [
41336         "Zimbabwe",
41337         "zw",
41338         "263"
41339       ],
41340       [
41341         "Åland Islands",
41342         "ax",
41343         "358",
41344         1
41345       ]
41346   ];
41347   
41348   return d;
41349 }/**
41350 *    This script refer to:
41351 *    Title: International Telephone Input
41352 *    Author: Jack O'Connor
41353 *    Code version:  v12.1.12
41354 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41355 **/
41356
41357 /**
41358  * @class Roo.bootstrap.PhoneInput
41359  * @extends Roo.bootstrap.TriggerField
41360  * An input with International dial-code selection
41361  
41362  * @cfg {String} defaultDialCode default '+852'
41363  * @cfg {Array} preferedCountries default []
41364   
41365  * @constructor
41366  * Create a new PhoneInput.
41367  * @param {Object} config Configuration options
41368  */
41369
41370 Roo.bootstrap.PhoneInput = function(config) {
41371     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41372 };
41373
41374 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41375         
41376         listWidth: undefined,
41377         
41378         selectedClass: 'active',
41379         
41380         invalidClass : "has-warning",
41381         
41382         validClass: 'has-success',
41383         
41384         allowed: '0123456789',
41385         
41386         max_length: 15,
41387         
41388         /**
41389          * @cfg {String} defaultDialCode The default dial code when initializing the input
41390          */
41391         defaultDialCode: '+852',
41392         
41393         /**
41394          * @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
41395          */
41396         preferedCountries: false,
41397         
41398         getAutoCreate : function()
41399         {
41400             var data = Roo.bootstrap.PhoneInputData();
41401             var align = this.labelAlign || this.parentLabelAlign();
41402             var id = Roo.id();
41403             
41404             this.allCountries = [];
41405             this.dialCodeMapping = [];
41406             
41407             for (var i = 0; i < data.length; i++) {
41408               var c = data[i];
41409               this.allCountries[i] = {
41410                 name: c[0],
41411                 iso2: c[1],
41412                 dialCode: c[2],
41413                 priority: c[3] || 0,
41414                 areaCodes: c[4] || null
41415               };
41416               this.dialCodeMapping[c[2]] = {
41417                   name: c[0],
41418                   iso2: c[1],
41419                   priority: c[3] || 0,
41420                   areaCodes: c[4] || null
41421               };
41422             }
41423             
41424             var cfg = {
41425                 cls: 'form-group',
41426                 cn: []
41427             };
41428             
41429             var input =  {
41430                 tag: 'input',
41431                 id : id,
41432                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41433                 maxlength: this.max_length,
41434                 cls : 'form-control tel-input',
41435                 autocomplete: 'new-password'
41436             };
41437             
41438             var hiddenInput = {
41439                 tag: 'input',
41440                 type: 'hidden',
41441                 cls: 'hidden-tel-input'
41442             };
41443             
41444             if (this.name) {
41445                 hiddenInput.name = this.name;
41446             }
41447             
41448             if (this.disabled) {
41449                 input.disabled = true;
41450             }
41451             
41452             var flag_container = {
41453                 tag: 'div',
41454                 cls: 'flag-box',
41455                 cn: [
41456                     {
41457                         tag: 'div',
41458                         cls: 'flag'
41459                     },
41460                     {
41461                         tag: 'div',
41462                         cls: 'caret'
41463                     }
41464                 ]
41465             };
41466             
41467             var box = {
41468                 tag: 'div',
41469                 cls: this.hasFeedback ? 'has-feedback' : '',
41470                 cn: [
41471                     hiddenInput,
41472                     input,
41473                     {
41474                         tag: 'input',
41475                         cls: 'dial-code-holder',
41476                         disabled: true
41477                     }
41478                 ]
41479             };
41480             
41481             var container = {
41482                 cls: 'roo-select2-container input-group',
41483                 cn: [
41484                     flag_container,
41485                     box
41486                 ]
41487             };
41488             
41489             if (this.fieldLabel.length) {
41490                 var indicator = {
41491                     tag: 'i',
41492                     tooltip: 'This field is required'
41493                 };
41494                 
41495                 var label = {
41496                     tag: 'label',
41497                     'for':  id,
41498                     cls: 'control-label',
41499                     cn: []
41500                 };
41501                 
41502                 var label_text = {
41503                     tag: 'span',
41504                     html: this.fieldLabel
41505                 };
41506                 
41507                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41508                 label.cn = [
41509                     indicator,
41510                     label_text
41511                 ];
41512                 
41513                 if(this.indicatorpos == 'right') {
41514                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41515                     label.cn = [
41516                         label_text,
41517                         indicator
41518                     ];
41519                 }
41520                 
41521                 if(align == 'left') {
41522                     container = {
41523                         tag: 'div',
41524                         cn: [
41525                             container
41526                         ]
41527                     };
41528                     
41529                     if(this.labelWidth > 12){
41530                         label.style = "width: " + this.labelWidth + 'px';
41531                     }
41532                     if(this.labelWidth < 13 && this.labelmd == 0){
41533                         this.labelmd = this.labelWidth;
41534                     }
41535                     if(this.labellg > 0){
41536                         label.cls += ' col-lg-' + this.labellg;
41537                         input.cls += ' col-lg-' + (12 - this.labellg);
41538                     }
41539                     if(this.labelmd > 0){
41540                         label.cls += ' col-md-' + this.labelmd;
41541                         container.cls += ' col-md-' + (12 - this.labelmd);
41542                     }
41543                     if(this.labelsm > 0){
41544                         label.cls += ' col-sm-' + this.labelsm;
41545                         container.cls += ' col-sm-' + (12 - this.labelsm);
41546                     }
41547                     if(this.labelxs > 0){
41548                         label.cls += ' col-xs-' + this.labelxs;
41549                         container.cls += ' col-xs-' + (12 - this.labelxs);
41550                     }
41551                 }
41552             }
41553             
41554             cfg.cn = [
41555                 label,
41556                 container
41557             ];
41558             
41559             var settings = this;
41560             
41561             ['xs','sm','md','lg'].map(function(size){
41562                 if (settings[size]) {
41563                     cfg.cls += ' col-' + size + '-' + settings[size];
41564                 }
41565             });
41566             
41567             this.store = new Roo.data.Store({
41568                 proxy : new Roo.data.MemoryProxy({}),
41569                 reader : new Roo.data.JsonReader({
41570                     fields : [
41571                         {
41572                             'name' : 'name',
41573                             'type' : 'string'
41574                         },
41575                         {
41576                             'name' : 'iso2',
41577                             'type' : 'string'
41578                         },
41579                         {
41580                             'name' : 'dialCode',
41581                             'type' : 'string'
41582                         },
41583                         {
41584                             'name' : 'priority',
41585                             'type' : 'string'
41586                         },
41587                         {
41588                             'name' : 'areaCodes',
41589                             'type' : 'string'
41590                         }
41591                     ]
41592                 })
41593             });
41594             
41595             if(!this.preferedCountries) {
41596                 this.preferedCountries = [
41597                     'hk',
41598                     'gb',
41599                     'us'
41600                 ];
41601             }
41602             
41603             var p = this.preferedCountries.reverse();
41604             
41605             if(p) {
41606                 for (var i = 0; i < p.length; i++) {
41607                     for (var j = 0; j < this.allCountries.length; j++) {
41608                         if(this.allCountries[j].iso2 == p[i]) {
41609                             var t = this.allCountries[j];
41610                             this.allCountries.splice(j,1);
41611                             this.allCountries.unshift(t);
41612                         }
41613                     } 
41614                 }
41615             }
41616             
41617             this.store.proxy.data = {
41618                 success: true,
41619                 data: this.allCountries
41620             };
41621             
41622             return cfg;
41623         },
41624         
41625         initEvents : function()
41626         {
41627             this.createList();
41628             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41629             
41630             this.indicator = this.indicatorEl();
41631             this.flag = this.flagEl();
41632             this.dialCodeHolder = this.dialCodeHolderEl();
41633             
41634             this.trigger = this.el.select('div.flag-box',true).first();
41635             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41636             
41637             var _this = this;
41638             
41639             (function(){
41640                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41641                 _this.list.setWidth(lw);
41642             }).defer(100);
41643             
41644             this.list.on('mouseover', this.onViewOver, this);
41645             this.list.on('mousemove', this.onViewMove, this);
41646             this.inputEl().on("keyup", this.onKeyUp, this);
41647             this.inputEl().on("keypress", this.onKeyPress, this);
41648             
41649             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41650
41651             this.view = new Roo.View(this.list, this.tpl, {
41652                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41653             });
41654             
41655             this.view.on('click', this.onViewClick, this);
41656             this.setValue(this.defaultDialCode);
41657         },
41658         
41659         onTriggerClick : function(e)
41660         {
41661             Roo.log('trigger click');
41662             if(this.disabled){
41663                 return;
41664             }
41665             
41666             if(this.isExpanded()){
41667                 this.collapse();
41668                 this.hasFocus = false;
41669             }else {
41670                 this.store.load({});
41671                 this.hasFocus = true;
41672                 this.expand();
41673             }
41674         },
41675         
41676         isExpanded : function()
41677         {
41678             return this.list.isVisible();
41679         },
41680         
41681         collapse : function()
41682         {
41683             if(!this.isExpanded()){
41684                 return;
41685             }
41686             this.list.hide();
41687             Roo.get(document).un('mousedown', this.collapseIf, this);
41688             Roo.get(document).un('mousewheel', this.collapseIf, this);
41689             this.fireEvent('collapse', this);
41690             this.validate();
41691         },
41692         
41693         expand : function()
41694         {
41695             Roo.log('expand');
41696
41697             if(this.isExpanded() || !this.hasFocus){
41698                 return;
41699             }
41700             
41701             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41702             this.list.setWidth(lw);
41703             
41704             this.list.show();
41705             this.restrictHeight();
41706             
41707             Roo.get(document).on('mousedown', this.collapseIf, this);
41708             Roo.get(document).on('mousewheel', this.collapseIf, this);
41709             
41710             this.fireEvent('expand', this);
41711         },
41712         
41713         restrictHeight : function()
41714         {
41715             this.list.alignTo(this.inputEl(), this.listAlign);
41716             this.list.alignTo(this.inputEl(), this.listAlign);
41717         },
41718         
41719         onViewOver : function(e, t)
41720         {
41721             if(this.inKeyMode){
41722                 return;
41723             }
41724             var item = this.view.findItemFromChild(t);
41725             
41726             if(item){
41727                 var index = this.view.indexOf(item);
41728                 this.select(index, false);
41729             }
41730         },
41731
41732         // private
41733         onViewClick : function(view, doFocus, el, e)
41734         {
41735             var index = this.view.getSelectedIndexes()[0];
41736             
41737             var r = this.store.getAt(index);
41738             
41739             if(r){
41740                 this.onSelect(r, index);
41741             }
41742             if(doFocus !== false && !this.blockFocus){
41743                 this.inputEl().focus();
41744             }
41745         },
41746         
41747         onViewMove : function(e, t)
41748         {
41749             this.inKeyMode = false;
41750         },
41751         
41752         select : function(index, scrollIntoView)
41753         {
41754             this.selectedIndex = index;
41755             this.view.select(index);
41756             if(scrollIntoView !== false){
41757                 var el = this.view.getNode(index);
41758                 if(el){
41759                     this.list.scrollChildIntoView(el, false);
41760                 }
41761             }
41762         },
41763         
41764         createList : function()
41765         {
41766             this.list = Roo.get(document.body).createChild({
41767                 tag: 'ul',
41768                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41769                 style: 'display:none'
41770             });
41771             
41772             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41773         },
41774         
41775         collapseIf : function(e)
41776         {
41777             var in_combo  = e.within(this.el);
41778             var in_list =  e.within(this.list);
41779             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41780             
41781             if (in_combo || in_list || is_list) {
41782                 return;
41783             }
41784             this.collapse();
41785         },
41786         
41787         onSelect : function(record, index)
41788         {
41789             if(this.fireEvent('beforeselect', this, record, index) !== false){
41790                 
41791                 this.setFlagClass(record.data.iso2);
41792                 this.setDialCode(record.data.dialCode);
41793                 this.hasFocus = false;
41794                 this.collapse();
41795                 this.fireEvent('select', this, record, index);
41796             }
41797         },
41798         
41799         flagEl : function()
41800         {
41801             var flag = this.el.select('div.flag',true).first();
41802             if(!flag){
41803                 return false;
41804             }
41805             return flag;
41806         },
41807         
41808         dialCodeHolderEl : function()
41809         {
41810             var d = this.el.select('input.dial-code-holder',true).first();
41811             if(!d){
41812                 return false;
41813             }
41814             return d;
41815         },
41816         
41817         setDialCode : function(v)
41818         {
41819             this.dialCodeHolder.dom.value = '+'+v;
41820         },
41821         
41822         setFlagClass : function(n)
41823         {
41824             this.flag.dom.className = 'flag '+n;
41825         },
41826         
41827         getValue : function()
41828         {
41829             var v = this.inputEl().getValue();
41830             if(this.dialCodeHolder) {
41831                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41832             }
41833             return v;
41834         },
41835         
41836         setValue : function(v)
41837         {
41838             var d = this.getDialCode(v);
41839             
41840             //invalid dial code
41841             if(v.length == 0 || !d || d.length == 0) {
41842                 if(this.rendered){
41843                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41844                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41845                 }
41846                 return;
41847             }
41848             
41849             //valid dial code
41850             this.setFlagClass(this.dialCodeMapping[d].iso2);
41851             this.setDialCode(d);
41852             this.inputEl().dom.value = v.replace('+'+d,'');
41853             this.hiddenEl().dom.value = this.getValue();
41854             
41855             this.validate();
41856         },
41857         
41858         getDialCode : function(v)
41859         {
41860             v = v ||  '';
41861             
41862             if (v.length == 0) {
41863                 return this.dialCodeHolder.dom.value;
41864             }
41865             
41866             var dialCode = "";
41867             if (v.charAt(0) != "+") {
41868                 return false;
41869             }
41870             var numericChars = "";
41871             for (var i = 1; i < v.length; i++) {
41872               var c = v.charAt(i);
41873               if (!isNaN(c)) {
41874                 numericChars += c;
41875                 if (this.dialCodeMapping[numericChars]) {
41876                   dialCode = v.substr(1, i);
41877                 }
41878                 if (numericChars.length == 4) {
41879                   break;
41880                 }
41881               }
41882             }
41883             return dialCode;
41884         },
41885         
41886         reset : function()
41887         {
41888             this.setValue(this.defaultDialCode);
41889             this.validate();
41890         },
41891         
41892         hiddenEl : function()
41893         {
41894             return this.el.select('input.hidden-tel-input',true).first();
41895         },
41896         
41897         // after setting val
41898         onKeyUp : function(e){
41899             this.setValue(this.getValue());
41900         },
41901         
41902         onKeyPress : function(e){
41903             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41904                 e.stopEvent();
41905             }
41906         }
41907         
41908 });
41909 /**
41910  * @class Roo.bootstrap.MoneyField
41911  * @extends Roo.bootstrap.ComboBox
41912  * Bootstrap MoneyField class
41913  * 
41914  * @constructor
41915  * Create a new MoneyField.
41916  * @param {Object} config Configuration options
41917  */
41918
41919 Roo.bootstrap.MoneyField = function(config) {
41920     
41921     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41922     
41923 };
41924
41925 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41926     
41927     /**
41928      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41929      */
41930     allowDecimals : true,
41931     /**
41932      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41933      */
41934     decimalSeparator : ".",
41935     /**
41936      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41937      */
41938     decimalPrecision : 0,
41939     /**
41940      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41941      */
41942     allowNegative : true,
41943     /**
41944      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41945      */
41946     allowZero: true,
41947     /**
41948      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41949      */
41950     minValue : Number.NEGATIVE_INFINITY,
41951     /**
41952      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41953      */
41954     maxValue : Number.MAX_VALUE,
41955     /**
41956      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41957      */
41958     minText : "The minimum value for this field is {0}",
41959     /**
41960      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41961      */
41962     maxText : "The maximum value for this field is {0}",
41963     /**
41964      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41965      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41966      */
41967     nanText : "{0} is not a valid number",
41968     /**
41969      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41970      */
41971     castInt : true,
41972     /**
41973      * @cfg {String} defaults currency of the MoneyField
41974      * value should be in lkey
41975      */
41976     defaultCurrency : false,
41977     /**
41978      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41979      */
41980     thousandsDelimiter : false,
41981     /**
41982      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41983      */
41984     max_length: false,
41985     
41986     inputlg : 9,
41987     inputmd : 9,
41988     inputsm : 9,
41989     inputxs : 6,
41990     
41991     store : false,
41992     
41993     getAutoCreate : function()
41994     {
41995         var align = this.labelAlign || this.parentLabelAlign();
41996         
41997         var id = Roo.id();
41998
41999         var cfg = {
42000             cls: 'form-group',
42001             cn: []
42002         };
42003
42004         var input =  {
42005             tag: 'input',
42006             id : id,
42007             cls : 'form-control roo-money-amount-input',
42008             autocomplete: 'new-password'
42009         };
42010         
42011         var hiddenInput = {
42012             tag: 'input',
42013             type: 'hidden',
42014             id: Roo.id(),
42015             cls: 'hidden-number-input'
42016         };
42017         
42018         if(this.max_length) {
42019             input.maxlength = this.max_length; 
42020         }
42021         
42022         if (this.name) {
42023             hiddenInput.name = this.name;
42024         }
42025
42026         if (this.disabled) {
42027             input.disabled = true;
42028         }
42029
42030         var clg = 12 - this.inputlg;
42031         var cmd = 12 - this.inputmd;
42032         var csm = 12 - this.inputsm;
42033         var cxs = 12 - this.inputxs;
42034         
42035         var container = {
42036             tag : 'div',
42037             cls : 'row roo-money-field',
42038             cn : [
42039                 {
42040                     tag : 'div',
42041                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42042                     cn : [
42043                         {
42044                             tag : 'div',
42045                             cls: 'roo-select2-container input-group',
42046                             cn: [
42047                                 {
42048                                     tag : 'input',
42049                                     cls : 'form-control roo-money-currency-input',
42050                                     autocomplete: 'new-password',
42051                                     readOnly : 1,
42052                                     name : this.currencyName
42053                                 },
42054                                 {
42055                                     tag :'span',
42056                                     cls : 'input-group-addon',
42057                                     cn : [
42058                                         {
42059                                             tag: 'span',
42060                                             cls: 'caret'
42061                                         }
42062                                     ]
42063                                 }
42064                             ]
42065                         }
42066                     ]
42067                 },
42068                 {
42069                     tag : 'div',
42070                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42071                     cn : [
42072                         {
42073                             tag: 'div',
42074                             cls: this.hasFeedback ? 'has-feedback' : '',
42075                             cn: [
42076                                 input
42077                             ]
42078                         }
42079                     ]
42080                 }
42081             ]
42082             
42083         };
42084         
42085         if (this.fieldLabel.length) {
42086             var indicator = {
42087                 tag: 'i',
42088                 tooltip: 'This field is required'
42089             };
42090
42091             var label = {
42092                 tag: 'label',
42093                 'for':  id,
42094                 cls: 'control-label',
42095                 cn: []
42096             };
42097
42098             var label_text = {
42099                 tag: 'span',
42100                 html: this.fieldLabel
42101             };
42102
42103             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42104             label.cn = [
42105                 indicator,
42106                 label_text
42107             ];
42108
42109             if(this.indicatorpos == 'right') {
42110                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42111                 label.cn = [
42112                     label_text,
42113                     indicator
42114                 ];
42115             }
42116
42117             if(align == 'left') {
42118                 container = {
42119                     tag: 'div',
42120                     cn: [
42121                         container
42122                     ]
42123                 };
42124
42125                 if(this.labelWidth > 12){
42126                     label.style = "width: " + this.labelWidth + 'px';
42127                 }
42128                 if(this.labelWidth < 13 && this.labelmd == 0){
42129                     this.labelmd = this.labelWidth;
42130                 }
42131                 if(this.labellg > 0){
42132                     label.cls += ' col-lg-' + this.labellg;
42133                     input.cls += ' col-lg-' + (12 - this.labellg);
42134                 }
42135                 if(this.labelmd > 0){
42136                     label.cls += ' col-md-' + this.labelmd;
42137                     container.cls += ' col-md-' + (12 - this.labelmd);
42138                 }
42139                 if(this.labelsm > 0){
42140                     label.cls += ' col-sm-' + this.labelsm;
42141                     container.cls += ' col-sm-' + (12 - this.labelsm);
42142                 }
42143                 if(this.labelxs > 0){
42144                     label.cls += ' col-xs-' + this.labelxs;
42145                     container.cls += ' col-xs-' + (12 - this.labelxs);
42146                 }
42147             }
42148         }
42149
42150         cfg.cn = [
42151             label,
42152             container,
42153             hiddenInput
42154         ];
42155         
42156         var settings = this;
42157
42158         ['xs','sm','md','lg'].map(function(size){
42159             if (settings[size]) {
42160                 cfg.cls += ' col-' + size + '-' + settings[size];
42161             }
42162         });
42163         
42164         return cfg;
42165     },
42166     
42167     initEvents : function()
42168     {
42169         this.indicator = this.indicatorEl();
42170         
42171         this.initCurrencyEvent();
42172         
42173         this.initNumberEvent();
42174     },
42175     
42176     initCurrencyEvent : function()
42177     {
42178         if (!this.store) {
42179             throw "can not find store for combo";
42180         }
42181         
42182         this.store = Roo.factory(this.store, Roo.data);
42183         this.store.parent = this;
42184         
42185         this.createList();
42186         
42187         this.triggerEl = this.el.select('.input-group-addon', true).first();
42188         
42189         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42190         
42191         var _this = this;
42192         
42193         (function(){
42194             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42195             _this.list.setWidth(lw);
42196         }).defer(100);
42197         
42198         this.list.on('mouseover', this.onViewOver, this);
42199         this.list.on('mousemove', this.onViewMove, this);
42200         this.list.on('scroll', this.onViewScroll, this);
42201         
42202         if(!this.tpl){
42203             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42204         }
42205         
42206         this.view = new Roo.View(this.list, this.tpl, {
42207             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42208         });
42209         
42210         this.view.on('click', this.onViewClick, this);
42211         
42212         this.store.on('beforeload', this.onBeforeLoad, this);
42213         this.store.on('load', this.onLoad, this);
42214         this.store.on('loadexception', this.onLoadException, this);
42215         
42216         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42217             "up" : function(e){
42218                 this.inKeyMode = true;
42219                 this.selectPrev();
42220             },
42221
42222             "down" : function(e){
42223                 if(!this.isExpanded()){
42224                     this.onTriggerClick();
42225                 }else{
42226                     this.inKeyMode = true;
42227                     this.selectNext();
42228                 }
42229             },
42230
42231             "enter" : function(e){
42232                 this.collapse();
42233                 
42234                 if(this.fireEvent("specialkey", this, e)){
42235                     this.onViewClick(false);
42236                 }
42237                 
42238                 return true;
42239             },
42240
42241             "esc" : function(e){
42242                 this.collapse();
42243             },
42244
42245             "tab" : function(e){
42246                 this.collapse();
42247                 
42248                 if(this.fireEvent("specialkey", this, e)){
42249                     this.onViewClick(false);
42250                 }
42251                 
42252                 return true;
42253             },
42254
42255             scope : this,
42256
42257             doRelay : function(foo, bar, hname){
42258                 if(hname == 'down' || this.scope.isExpanded()){
42259                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42260                 }
42261                 return true;
42262             },
42263
42264             forceKeyDown: true
42265         });
42266         
42267         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42268         
42269     },
42270     
42271     initNumberEvent : function(e)
42272     {
42273         this.inputEl().on("keydown" , this.fireKey,  this);
42274         this.inputEl().on("focus", this.onFocus,  this);
42275         this.inputEl().on("blur", this.onBlur,  this);
42276         
42277         this.inputEl().relayEvent('keyup', this);
42278         
42279         if(this.indicator){
42280             this.indicator.addClass('invisible');
42281         }
42282  
42283         this.originalValue = this.getValue();
42284         
42285         if(this.validationEvent == 'keyup'){
42286             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42287             this.inputEl().on('keyup', this.filterValidation, this);
42288         }
42289         else if(this.validationEvent !== false){
42290             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42291         }
42292         
42293         if(this.selectOnFocus){
42294             this.on("focus", this.preFocus, this);
42295             
42296         }
42297         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42298             this.inputEl().on("keypress", this.filterKeys, this);
42299         } else {
42300             this.inputEl().relayEvent('keypress', this);
42301         }
42302         
42303         var allowed = "0123456789";
42304         
42305         if(this.allowDecimals){
42306             allowed += this.decimalSeparator;
42307         }
42308         
42309         if(this.allowNegative){
42310             allowed += "-";
42311         }
42312         
42313         if(this.thousandsDelimiter) {
42314             allowed += ",";
42315         }
42316         
42317         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42318         
42319         var keyPress = function(e){
42320             
42321             var k = e.getKey();
42322             
42323             var c = e.getCharCode();
42324             
42325             if(
42326                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42327                     allowed.indexOf(String.fromCharCode(c)) === -1
42328             ){
42329                 e.stopEvent();
42330                 return;
42331             }
42332             
42333             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42334                 return;
42335             }
42336             
42337             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42338                 e.stopEvent();
42339             }
42340         };
42341         
42342         this.inputEl().on("keypress", keyPress, this);
42343         
42344     },
42345     
42346     onTriggerClick : function(e)
42347     {   
42348         if(this.disabled){
42349             return;
42350         }
42351         
42352         this.page = 0;
42353         this.loadNext = false;
42354         
42355         if(this.isExpanded()){
42356             this.collapse();
42357             return;
42358         }
42359         
42360         this.hasFocus = true;
42361         
42362         if(this.triggerAction == 'all') {
42363             this.doQuery(this.allQuery, true);
42364             return;
42365         }
42366         
42367         this.doQuery(this.getRawValue());
42368     },
42369     
42370     getCurrency : function()
42371     {   
42372         var v = this.currencyEl().getValue();
42373         
42374         return v;
42375     },
42376     
42377     restrictHeight : function()
42378     {
42379         this.list.alignTo(this.currencyEl(), this.listAlign);
42380         this.list.alignTo(this.currencyEl(), this.listAlign);
42381     },
42382     
42383     onViewClick : function(view, doFocus, el, e)
42384     {
42385         var index = this.view.getSelectedIndexes()[0];
42386         
42387         var r = this.store.getAt(index);
42388         
42389         if(r){
42390             this.onSelect(r, index);
42391         }
42392     },
42393     
42394     onSelect : function(record, index){
42395         
42396         if(this.fireEvent('beforeselect', this, record, index) !== false){
42397         
42398             this.setFromCurrencyData(index > -1 ? record.data : false);
42399             
42400             this.collapse();
42401             
42402             this.fireEvent('select', this, record, index);
42403         }
42404     },
42405     
42406     setFromCurrencyData : function(o)
42407     {
42408         var currency = '';
42409         
42410         this.lastCurrency = o;
42411         
42412         if (this.currencyField) {
42413             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42414         } else {
42415             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42416         }
42417         
42418         this.lastSelectionText = currency;
42419         
42420         //setting default currency
42421         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42422             this.setCurrency(this.defaultCurrency);
42423             return;
42424         }
42425         
42426         this.setCurrency(currency);
42427     },
42428     
42429     setFromData : function(o)
42430     {
42431         var c = {};
42432         
42433         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42434         
42435         this.setFromCurrencyData(c);
42436         
42437         var value = '';
42438         
42439         if (this.name) {
42440             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42441         } else {
42442             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42443         }
42444         
42445         this.setValue(value);
42446         
42447     },
42448     
42449     setCurrency : function(v)
42450     {   
42451         this.currencyValue = v;
42452         
42453         if(this.rendered){
42454             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42455             this.validate();
42456         }
42457     },
42458     
42459     setValue : function(v)
42460     {
42461         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42462         
42463         this.value = v;
42464         
42465         if(this.rendered){
42466             
42467             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42468             
42469             this.inputEl().dom.value = (v == '') ? '' :
42470                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42471             
42472             if(!this.allowZero && v === '0') {
42473                 this.hiddenEl().dom.value = '';
42474                 this.inputEl().dom.value = '';
42475             }
42476             
42477             this.validate();
42478         }
42479     },
42480     
42481     getRawValue : function()
42482     {
42483         var v = this.inputEl().getValue();
42484         
42485         return v;
42486     },
42487     
42488     getValue : function()
42489     {
42490         return this.fixPrecision(this.parseValue(this.getRawValue()));
42491     },
42492     
42493     parseValue : function(value)
42494     {
42495         if(this.thousandsDelimiter) {
42496             value += "";
42497             r = new RegExp(",", "g");
42498             value = value.replace(r, "");
42499         }
42500         
42501         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42502         return isNaN(value) ? '' : value;
42503         
42504     },
42505     
42506     fixPrecision : function(value)
42507     {
42508         if(this.thousandsDelimiter) {
42509             value += "";
42510             r = new RegExp(",", "g");
42511             value = value.replace(r, "");
42512         }
42513         
42514         var nan = isNaN(value);
42515         
42516         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42517             return nan ? '' : value;
42518         }
42519         return parseFloat(value).toFixed(this.decimalPrecision);
42520     },
42521     
42522     decimalPrecisionFcn : function(v)
42523     {
42524         return Math.floor(v);
42525     },
42526     
42527     validateValue : function(value)
42528     {
42529         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42530             return false;
42531         }
42532         
42533         var num = this.parseValue(value);
42534         
42535         if(isNaN(num)){
42536             this.markInvalid(String.format(this.nanText, value));
42537             return false;
42538         }
42539         
42540         if(num < this.minValue){
42541             this.markInvalid(String.format(this.minText, this.minValue));
42542             return false;
42543         }
42544         
42545         if(num > this.maxValue){
42546             this.markInvalid(String.format(this.maxText, this.maxValue));
42547             return false;
42548         }
42549         
42550         return true;
42551     },
42552     
42553     validate : function()
42554     {
42555         if(this.disabled || this.allowBlank){
42556             this.markValid();
42557             return true;
42558         }
42559         
42560         var currency = this.getCurrency();
42561         
42562         if(this.validateValue(this.getRawValue()) && currency.length){
42563             this.markValid();
42564             return true;
42565         }
42566         
42567         this.markInvalid();
42568         return false;
42569     },
42570     
42571     getName: function()
42572     {
42573         return this.name;
42574     },
42575     
42576     beforeBlur : function()
42577     {
42578         if(!this.castInt){
42579             return;
42580         }
42581         
42582         var v = this.parseValue(this.getRawValue());
42583         
42584         if(v || v == 0){
42585             this.setValue(v);
42586         }
42587     },
42588     
42589     onBlur : function()
42590     {
42591         this.beforeBlur();
42592         
42593         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42594             //this.el.removeClass(this.focusClass);
42595         }
42596         
42597         this.hasFocus = false;
42598         
42599         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42600             this.validate();
42601         }
42602         
42603         var v = this.getValue();
42604         
42605         if(String(v) !== String(this.startValue)){
42606             this.fireEvent('change', this, v, this.startValue);
42607         }
42608         
42609         this.fireEvent("blur", this);
42610     },
42611     
42612     inputEl : function()
42613     {
42614         return this.el.select('.roo-money-amount-input', true).first();
42615     },
42616     
42617     currencyEl : function()
42618     {
42619         return this.el.select('.roo-money-currency-input', true).first();
42620     },
42621     
42622     hiddenEl : function()
42623     {
42624         return this.el.select('input.hidden-number-input',true).first();
42625     }
42626     
42627 });/**
42628  * @class Roo.bootstrap.BezierSignature
42629  * @extends Roo.bootstrap.Component
42630  * Bootstrap BezierSignature class
42631  * This script refer to:
42632  *    Title: Signature Pad
42633  *    Author: szimek
42634  *    Availability: https://github.com/szimek/signature_pad
42635  *
42636  * @constructor
42637  * Create a new BezierSignature
42638  * @param {Object} config The config object
42639  */
42640
42641 Roo.bootstrap.BezierSignature = function(config){
42642     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42643     this.addEvents({
42644         "resize" : true
42645     });
42646 };
42647
42648 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42649 {
42650      
42651     curve_data: [],
42652     
42653     is_empty: true,
42654     
42655     mouse_btn_down: true,
42656     
42657     /**
42658      * @cfg {int} canvas height
42659      */
42660     canvas_height: '200px',
42661     
42662     /**
42663      * @cfg {float|function} Radius of a single dot.
42664      */ 
42665     dot_size: false,
42666     
42667     /**
42668      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42669      */
42670     min_width: 0.5,
42671     
42672     /**
42673      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42674      */
42675     max_width: 2.5,
42676     
42677     /**
42678      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42679      */
42680     throttle: 16,
42681     
42682     /**
42683      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42684      */
42685     min_distance: 5,
42686     
42687     /**
42688      * @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.
42689      */
42690     bg_color: 'rgba(0, 0, 0, 0)',
42691     
42692     /**
42693      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42694      */
42695     dot_color: 'black',
42696     
42697     /**
42698      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42699      */ 
42700     velocity_filter_weight: 0.7,
42701     
42702     /**
42703      * @cfg {function} Callback when stroke begin. 
42704      */
42705     onBegin: false,
42706     
42707     /**
42708      * @cfg {function} Callback when stroke end.
42709      */
42710     onEnd: false,
42711     
42712     getAutoCreate : function()
42713     {
42714         var cls = 'roo-signature column';
42715         
42716         if(this.cls){
42717             cls += ' ' + this.cls;
42718         }
42719         
42720         var col_sizes = [
42721             'lg',
42722             'md',
42723             'sm',
42724             'xs'
42725         ];
42726         
42727         for(var i = 0; i < col_sizes.length; i++) {
42728             if(this[col_sizes[i]]) {
42729                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42730             }
42731         }
42732         
42733         var cfg = {
42734             tag: 'div',
42735             cls: cls,
42736             cn: [
42737                 {
42738                     tag: 'div',
42739                     cls: 'roo-signature-body',
42740                     cn: [
42741                         {
42742                             tag: 'canvas',
42743                             cls: 'roo-signature-body-canvas',
42744                             height: this.canvas_height,
42745                             width: this.canvas_width
42746                         }
42747                     ]
42748                 },
42749                 {
42750                     tag: 'input',
42751                     type: 'file',
42752                     style: 'display: none'
42753                 }
42754             ]
42755         };
42756         
42757         return cfg;
42758     },
42759     
42760     initEvents: function() 
42761     {
42762         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42763         
42764         var canvas = this.canvasEl();
42765         
42766         // mouse && touch event swapping...
42767         canvas.dom.style.touchAction = 'none';
42768         canvas.dom.style.msTouchAction = 'none';
42769         
42770         this.mouse_btn_down = false;
42771         canvas.on('mousedown', this._handleMouseDown, this);
42772         canvas.on('mousemove', this._handleMouseMove, this);
42773         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42774         
42775         if (window.PointerEvent) {
42776             canvas.on('pointerdown', this._handleMouseDown, this);
42777             canvas.on('pointermove', this._handleMouseMove, this);
42778             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42779         }
42780         
42781         if ('ontouchstart' in window) {
42782             canvas.on('touchstart', this._handleTouchStart, this);
42783             canvas.on('touchmove', this._handleTouchMove, this);
42784             canvas.on('touchend', this._handleTouchEnd, this);
42785         }
42786         
42787         Roo.EventManager.onWindowResize(this.resize, this, true);
42788         
42789         // file input event
42790         this.fileEl().on('change', this.uploadImage, this);
42791         
42792         this.clear();
42793         
42794         this.resize();
42795     },
42796     
42797     resize: function(){
42798         
42799         var canvas = this.canvasEl().dom;
42800         var ctx = this.canvasElCtx();
42801         var img_data = false;
42802         
42803         if(canvas.width > 0) {
42804             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42805         }
42806         // setting canvas width will clean img data
42807         canvas.width = 0;
42808         
42809         var style = window.getComputedStyle ? 
42810             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42811             
42812         var padding_left = parseInt(style.paddingLeft) || 0;
42813         var padding_right = parseInt(style.paddingRight) || 0;
42814         
42815         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42816         
42817         if(img_data) {
42818             ctx.putImageData(img_data, 0, 0);
42819         }
42820     },
42821     
42822     _handleMouseDown: function(e)
42823     {
42824         if (e.browserEvent.which === 1) {
42825             this.mouse_btn_down = true;
42826             this.strokeBegin(e);
42827         }
42828     },
42829     
42830     _handleMouseMove: function (e)
42831     {
42832         if (this.mouse_btn_down) {
42833             this.strokeMoveUpdate(e);
42834         }
42835     },
42836     
42837     _handleMouseUp: function (e)
42838     {
42839         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42840             this.mouse_btn_down = false;
42841             this.strokeEnd(e);
42842         }
42843     },
42844     
42845     _handleTouchStart: function (e) {
42846         
42847         e.preventDefault();
42848         if (e.browserEvent.targetTouches.length === 1) {
42849             // var touch = e.browserEvent.changedTouches[0];
42850             // this.strokeBegin(touch);
42851             
42852              this.strokeBegin(e); // assume e catching the correct xy...
42853         }
42854     },
42855     
42856     _handleTouchMove: function (e) {
42857         e.preventDefault();
42858         // var touch = event.targetTouches[0];
42859         // _this._strokeMoveUpdate(touch);
42860         this.strokeMoveUpdate(e);
42861     },
42862     
42863     _handleTouchEnd: function (e) {
42864         var wasCanvasTouched = e.target === this.canvasEl().dom;
42865         if (wasCanvasTouched) {
42866             e.preventDefault();
42867             // var touch = event.changedTouches[0];
42868             // _this._strokeEnd(touch);
42869             this.strokeEnd(e);
42870         }
42871     },
42872     
42873     reset: function () {
42874         this._lastPoints = [];
42875         this._lastVelocity = 0;
42876         this._lastWidth = (this.min_width + this.max_width) / 2;
42877         this.canvasElCtx().fillStyle = this.dot_color;
42878     },
42879     
42880     strokeMoveUpdate: function(e)
42881     {
42882         this.strokeUpdate(e);
42883         
42884         if (this.throttle) {
42885             this.throttleStroke(this.strokeUpdate, this.throttle);
42886         }
42887         else {
42888             this.strokeUpdate(e);
42889         }
42890     },
42891     
42892     strokeBegin: function(e)
42893     {
42894         var newPointGroup = {
42895             color: this.dot_color,
42896             points: []
42897         };
42898         
42899         if (typeof this.onBegin === 'function') {
42900             this.onBegin(e);
42901         }
42902         
42903         this.curve_data.push(newPointGroup);
42904         this.reset();
42905         this.strokeUpdate(e);
42906     },
42907     
42908     strokeUpdate: function(e)
42909     {
42910         var rect = this.canvasEl().dom.getBoundingClientRect();
42911         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42912         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42913         var lastPoints = lastPointGroup.points;
42914         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42915         var isLastPointTooClose = lastPoint
42916             ? point.distanceTo(lastPoint) <= this.min_distance
42917             : false;
42918         var color = lastPointGroup.color;
42919         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42920             var curve = this.addPoint(point);
42921             if (!lastPoint) {
42922                 this.drawDot({color: color, point: point});
42923             }
42924             else if (curve) {
42925                 this.drawCurve({color: color, curve: curve});
42926             }
42927             lastPoints.push({
42928                 time: point.time,
42929                 x: point.x,
42930                 y: point.y
42931             });
42932         }
42933     },
42934     
42935     strokeEnd: function(e)
42936     {
42937         this.strokeUpdate(e);
42938         if (typeof this.onEnd === 'function') {
42939             this.onEnd(e);
42940         }
42941     },
42942     
42943     addPoint:  function (point) {
42944         var _lastPoints = this._lastPoints;
42945         _lastPoints.push(point);
42946         if (_lastPoints.length > 2) {
42947             if (_lastPoints.length === 3) {
42948                 _lastPoints.unshift(_lastPoints[0]);
42949             }
42950             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42951             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42952             _lastPoints.shift();
42953             return curve;
42954         }
42955         return null;
42956     },
42957     
42958     calculateCurveWidths: function (startPoint, endPoint) {
42959         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42960             (1 - this.velocity_filter_weight) * this._lastVelocity;
42961
42962         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42963         var widths = {
42964             end: newWidth,
42965             start: this._lastWidth
42966         };
42967         
42968         this._lastVelocity = velocity;
42969         this._lastWidth = newWidth;
42970         return widths;
42971     },
42972     
42973     drawDot: function (_a) {
42974         var color = _a.color, point = _a.point;
42975         var ctx = this.canvasElCtx();
42976         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42977         ctx.beginPath();
42978         this.drawCurveSegment(point.x, point.y, width);
42979         ctx.closePath();
42980         ctx.fillStyle = color;
42981         ctx.fill();
42982     },
42983     
42984     drawCurve: function (_a) {
42985         var color = _a.color, curve = _a.curve;
42986         var ctx = this.canvasElCtx();
42987         var widthDelta = curve.endWidth - curve.startWidth;
42988         var drawSteps = Math.floor(curve.length()) * 2;
42989         ctx.beginPath();
42990         ctx.fillStyle = color;
42991         for (var i = 0; i < drawSteps; i += 1) {
42992         var t = i / drawSteps;
42993         var tt = t * t;
42994         var ttt = tt * t;
42995         var u = 1 - t;
42996         var uu = u * u;
42997         var uuu = uu * u;
42998         var x = uuu * curve.startPoint.x;
42999         x += 3 * uu * t * curve.control1.x;
43000         x += 3 * u * tt * curve.control2.x;
43001         x += ttt * curve.endPoint.x;
43002         var y = uuu * curve.startPoint.y;
43003         y += 3 * uu * t * curve.control1.y;
43004         y += 3 * u * tt * curve.control2.y;
43005         y += ttt * curve.endPoint.y;
43006         var width = curve.startWidth + ttt * widthDelta;
43007         this.drawCurveSegment(x, y, width);
43008         }
43009         ctx.closePath();
43010         ctx.fill();
43011     },
43012     
43013     drawCurveSegment: function (x, y, width) {
43014         var ctx = this.canvasElCtx();
43015         ctx.moveTo(x, y);
43016         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43017         this.is_empty = false;
43018     },
43019     
43020     clear: function()
43021     {
43022         var ctx = this.canvasElCtx();
43023         var canvas = this.canvasEl().dom;
43024         ctx.fillStyle = this.bg_color;
43025         ctx.clearRect(0, 0, canvas.width, canvas.height);
43026         ctx.fillRect(0, 0, canvas.width, canvas.height);
43027         this.curve_data = [];
43028         this.reset();
43029         this.is_empty = true;
43030     },
43031     
43032     fileEl: function()
43033     {
43034         return  this.el.select('input',true).first();
43035     },
43036     
43037     canvasEl: function()
43038     {
43039         return this.el.select('canvas',true).first();
43040     },
43041     
43042     canvasElCtx: function()
43043     {
43044         return this.el.select('canvas',true).first().dom.getContext('2d');
43045     },
43046     
43047     getImage: function(type)
43048     {
43049         if(this.is_empty) {
43050             return false;
43051         }
43052         
43053         // encryption ?
43054         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43055     },
43056     
43057     drawFromImage: function(img_src)
43058     {
43059         var img = new Image();
43060         
43061         img.onload = function(){
43062             this.canvasElCtx().drawImage(img, 0, 0);
43063         }.bind(this);
43064         
43065         img.src = img_src;
43066         
43067         this.is_empty = false;
43068     },
43069     
43070     selectImage: function()
43071     {
43072         this.fileEl().dom.click();
43073     },
43074     
43075     uploadImage: function(e)
43076     {
43077         var reader = new FileReader();
43078         
43079         reader.onload = function(e){
43080             var img = new Image();
43081             img.onload = function(){
43082                 this.reset();
43083                 this.canvasElCtx().drawImage(img, 0, 0);
43084             }.bind(this);
43085             img.src = e.target.result;
43086         }.bind(this);
43087         
43088         reader.readAsDataURL(e.target.files[0]);
43089     },
43090     
43091     // Bezier Point Constructor
43092     Point: (function () {
43093         function Point(x, y, time) {
43094             this.x = x;
43095             this.y = y;
43096             this.time = time || Date.now();
43097         }
43098         Point.prototype.distanceTo = function (start) {
43099             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43100         };
43101         Point.prototype.equals = function (other) {
43102             return this.x === other.x && this.y === other.y && this.time === other.time;
43103         };
43104         Point.prototype.velocityFrom = function (start) {
43105             return this.time !== start.time
43106             ? this.distanceTo(start) / (this.time - start.time)
43107             : 0;
43108         };
43109         return Point;
43110     }()),
43111     
43112     
43113     // Bezier Constructor
43114     Bezier: (function () {
43115         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43116             this.startPoint = startPoint;
43117             this.control2 = control2;
43118             this.control1 = control1;
43119             this.endPoint = endPoint;
43120             this.startWidth = startWidth;
43121             this.endWidth = endWidth;
43122         }
43123         Bezier.fromPoints = function (points, widths, scope) {
43124             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43125             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43126             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43127         };
43128         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43129             var dx1 = s1.x - s2.x;
43130             var dy1 = s1.y - s2.y;
43131             var dx2 = s2.x - s3.x;
43132             var dy2 = s2.y - s3.y;
43133             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43134             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43135             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43136             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43137             var dxm = m1.x - m2.x;
43138             var dym = m1.y - m2.y;
43139             var k = l2 / (l1 + l2);
43140             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43141             var tx = s2.x - cm.x;
43142             var ty = s2.y - cm.y;
43143             return {
43144                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43145                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43146             };
43147         };
43148         Bezier.prototype.length = function () {
43149             var steps = 10;
43150             var length = 0;
43151             var px;
43152             var py;
43153             for (var i = 0; i <= steps; i += 1) {
43154                 var t = i / steps;
43155                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43156                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43157                 if (i > 0) {
43158                     var xdiff = cx - px;
43159                     var ydiff = cy - py;
43160                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43161                 }
43162                 px = cx;
43163                 py = cy;
43164             }
43165             return length;
43166         };
43167         Bezier.prototype.point = function (t, start, c1, c2, end) {
43168             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43169             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43170             + (3.0 * c2 * (1.0 - t) * t * t)
43171             + (end * t * t * t);
43172         };
43173         return Bezier;
43174     }()),
43175     
43176     throttleStroke: function(fn, wait) {
43177       if (wait === void 0) { wait = 250; }
43178       var previous = 0;
43179       var timeout = null;
43180       var result;
43181       var storedContext;
43182       var storedArgs;
43183       var later = function () {
43184           previous = Date.now();
43185           timeout = null;
43186           result = fn.apply(storedContext, storedArgs);
43187           if (!timeout) {
43188               storedContext = null;
43189               storedArgs = [];
43190           }
43191       };
43192       return function wrapper() {
43193           var args = [];
43194           for (var _i = 0; _i < arguments.length; _i++) {
43195               args[_i] = arguments[_i];
43196           }
43197           var now = Date.now();
43198           var remaining = wait - (now - previous);
43199           storedContext = this;
43200           storedArgs = args;
43201           if (remaining <= 0 || remaining > wait) {
43202               if (timeout) {
43203                   clearTimeout(timeout);
43204                   timeout = null;
43205               }
43206               previous = now;
43207               result = fn.apply(storedContext, storedArgs);
43208               if (!timeout) {
43209                   storedContext = null;
43210                   storedArgs = [];
43211               }
43212           }
43213           else if (!timeout) {
43214               timeout = window.setTimeout(later, remaining);
43215           }
43216           return result;
43217       };
43218   }
43219   
43220 });
43221
43222  
43223
43224