28ed9282cef851d936097b2b7822179d9efb629a
[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         
1940     });
1941 };
1942
1943
1944 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1945     
1946     
1947     weight : '',
1948     
1949     margin: '', /// may be better in component?
1950     margin_top: '', 
1951     margin_bottom: '', 
1952     margin_left: '',
1953     margin_right: '',
1954     margin_x: '',
1955     margin_y: '',
1956     
1957     padding : '',
1958     padding_top: '', 
1959     padding_bottom: '', 
1960     padding_left: '',
1961     padding_right: '',
1962     padding_x: '',
1963     padding_y: '',
1964     
1965     display: '', 
1966     display_xs: '', 
1967     display_sm: '', 
1968     display_lg: '',
1969     display_xl: '',
1970  
1971     header_image  : '',
1972     header : '',
1973     header_size : 0,
1974     title : '',
1975     subtitle : '',
1976     html : '',
1977     footer: '',
1978
1979     collapsable : false,
1980     collapsed : false,
1981     
1982     dragable : false,
1983     drag_group : false,
1984     dropable : false,
1985     drop_group : false,
1986     childContainer : false,
1987
1988     layoutCls : function()
1989     {
1990         var cls = '';
1991         var t = this;
1992         Roo.log(this.margin_bottom.length);
1993         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1994             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1995             
1996             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1997                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1998             }
1999             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2000                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2001             }
2002         });
2003         
2004         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2005             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2006                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2007             }
2008         });
2009         
2010         // more generic support?
2011         if (this.hidden) {
2012             cls += ' d-none';
2013         }
2014         
2015         return cls;
2016     },
2017  
2018        // Roo.log("Call onRender: " + this.xtype);
2019         /*  We are looking at something like this.
2020 <div class="card">
2021     <img src="..." class="card-img-top" alt="...">
2022     <div class="card-body">
2023         <h5 class="card-title">Card title</h5>
2024          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2025
2026         >> this bit is really the body...
2027         <div> << we will ad dthis in hopefully it will not break shit.
2028         
2029         ** card text does not actually have any styling...
2030         
2031             <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>
2032         
2033         </div> <<
2034           <a href="#" class="card-link">Card link</a>
2035           
2036     </div>
2037     <div class="card-footer">
2038         <small class="text-muted">Last updated 3 mins ago</small>
2039     </div>
2040 </div>
2041          */
2042     getAutoCreate : function(){
2043         
2044         var cfg = {
2045             tag : 'div',
2046             cls : 'card',
2047             cn : [ ]
2048         };
2049         
2050         if (this.weight.length && this.weight != 'light') {
2051             cfg.cls += ' text-white';
2052         } else {
2053             cfg.cls += ' text-dark'; // need as it's nested..
2054         }
2055         if (this.weight.length) {
2056             cfg.cls += ' bg-' + this.weight;
2057         }
2058         
2059         cfg.cls += this.layoutCls(); 
2060         
2061         var hdr = false;
2062         if (this.header.length) {
2063             hdr = {
2064                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2065                 cls : 'card-header',
2066                 cn : []
2067             };
2068             cfg.cn.push(hdr);
2069             hdr_ctr = hdr;
2070         } else {
2071             hdr = {
2072                 tag : 'div',
2073                 cls : 'card-header d-none',
2074                 cn : []
2075             };
2076             cfg.cn.push(hdr);
2077         }
2078         if (this.collapsable) {
2079             hdr_ctr = {
2080                 tag : 'a',
2081                 cls : 'd-block user-select-none',
2082                 cn: [
2083                     {
2084                         tag: 'i',
2085                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right'
2086                     }
2087                    
2088                 ]
2089             };
2090             hdr.cn.push(hdr_ctr);
2091         }
2092         if (this.header.length) {
2093             hdr_ctr.cn.push(        {
2094                 tag: 'span',
2095                 cls: 'roo-card-header-ctr',
2096                 html : this.header
2097             })
2098         }
2099         
2100         if (this.header_image.length) {
2101             cfg.cn.push({
2102                 tag : 'img',
2103                 cls : 'card-img-top',
2104                 src: this.header_image // escape?
2105             });
2106         } else {
2107             cfg.cn.push({
2108                 tag : 'div',
2109                 cls : 'card-img-top d-none' 
2110             });
2111         }
2112         
2113         var body = {
2114             tag : 'div',
2115             cls : 'card-body',
2116             cn : []
2117         };
2118         var obody = body;
2119         if (this.collapsable) {
2120             obody = {
2121                 tag: 'div',
2122                 cls : 'roo-collapsable collapse ' + (this.collapsed ? '' : 'show'),
2123                 cn : [  body ]
2124             };
2125         }
2126         
2127         cfg.cn.push(obody);
2128         
2129         if (this.title.length) {
2130             body.cn.push({
2131                 tag : 'div',
2132                 cls : 'card-title',
2133                 src: this.title // escape?
2134             });
2135         }
2136         
2137         if (this.subtitle.length) {
2138             body.cn.push({
2139                 tag : 'div',
2140                 cls : 'card-title',
2141                 src: this.subtitle // escape?
2142             });
2143         }
2144         
2145         body.cn.push({
2146             tag : 'div',
2147             cls : 'roo-card-body-ctr'
2148         });
2149         
2150         if (this.html.length) {
2151             body.cn.push({
2152                 tag: 'div',
2153                 html : this.html
2154             });
2155         }
2156         // fixme ? handle objects?
2157         if (this.footer.length) {
2158             cfg.cn.push({
2159                 tag : 'div',
2160                 cls : 'card-footer',
2161                 html: this.footer // escape?
2162             });
2163         }
2164         // footer...
2165         
2166         return cfg;
2167     },
2168     
2169     
2170     getCardHeader : function()
2171     {
2172         var  ret = this.el.select('.card-header',true).first();
2173         if (ret.hasClass('d-none')) {
2174             ret.removeClass('d-none');
2175         }
2176         
2177         return ret;
2178     },
2179     
2180     getCardImageTop : function()
2181     {
2182         var  ret = this.el.select('.card-img-top',true).first();
2183         if (ret.hasClass('d-none')) {
2184             ret.removeClass('d-none');
2185         }
2186         
2187         return ret;
2188     },
2189     
2190     getChildContainer : function()
2191     {
2192         
2193         if(!this.el){
2194             return false;
2195         }
2196         return this.el.select('.roo-card-body-ctr',true).first();    
2197     },
2198     
2199     initEvents: function() 
2200     {
2201         
2202         this.bodyEl = this.getChildContainer();
2203         if(this.dragable){
2204             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2205                     containerScroll: true,
2206                     ddGroup: this.drag_group || 'default_card_drag_group'
2207             });
2208             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2209         }
2210         if (this.dropable) {
2211             this.dropZone = new Roo.dd.DropZone(this.getChildContainer(), {
2212                     containerScroll: true,
2213                     ddGroup: this.drop_group || 'default_card_drag_group'
2214             });
2215             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2216             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2217             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2218             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2219             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2220         }
2221         
2222         if (this.collapsable) {
2223             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2224         }
2225     },
2226     getDragData : function(e) {
2227         var target = this.getEl();
2228         if (target) {
2229             //this.handleSelection(e);
2230             
2231             var dragData = {
2232                 source: this,
2233                 copy: false,
2234                 nodes: this.getEl(),
2235                 records: []
2236             };
2237             
2238             
2239             dragData.ddel = target.dom ;        // the div element
2240             Roo.log(target.getWidth( ));
2241              dragData.ddel.style.width = target.getWidth() + 'px';
2242             
2243             return dragData;
2244         }
2245         return false;
2246     },
2247     /**
2248  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
2249  *      whole Element becomes the target, and this causes the drop gesture to append.
2250  */
2251     getTargetFromEvent : function(e)
2252     {
2253         var target = e.getTarget();
2254         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2255             target = target.parentNode;
2256         }
2257         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2258         // see if target is one of the 'cards'...
2259         var ctarget = false;
2260         var cards = [];
2261         //Roo.log(this.items.length);
2262         var lpos = pos = false;
2263         for (var i = 0;i< this.items.length;i++) {
2264             
2265             if (!this.items[i].el.hasClass('card')) {
2266                 continue;
2267             }
2268             pos = this.getDropPoint(e, this.items[i].el.dom);
2269             
2270             //Roo.log(this.items[i].el.dom.id);
2271             cards.push(this.items[i]);
2272             if (pos == 'above') {
2273                 ctarget = this.items[i > 0 ? i-1 : 0];
2274                 pos = i > 0 ? 'below' : pos;
2275                 break;
2276             }
2277         }
2278         
2279         if (!ctarget) {
2280             ctarget = cards[cards.length-1] || this.el.dom;
2281             pos = 'below'
2282         }
2283         
2284         
2285         //Roo.log(['getTargetFromEvent', ctarget]);
2286         return [ ctarget, pos ];
2287     },
2288     
2289     onNodeEnter : function(n, dd, e, data){
2290         return false;
2291     },
2292     onNodeOver : function(n, dd, e, data)
2293     {
2294 //      Roo.log(['onNodeOver'])
2295         /*
2296         var pt = this.getDropPoint(e, n, dd);
2297         // set the insert point style on the target node
2298         //var dragElClass = this.dropNotAllowed;
2299         if (!pt) {
2300             this.dropPlaceHolder('hide');
2301             return false;
2302             
2303         }
2304         */
2305         target_info = this.getTargetFromEvent(e);
2306         Roo.log(['getTargetFromEvent', target_info[0].el.dom.id,target_info[1]]);
2307         
2308         
2309         this.dropPlaceHolder('show', targetinfo,data);
2310         return false; //dragElClass;
2311     },
2312     onNodeOut : function(n, dd, e, data){
2313         //this.removeDropIndicators(n);
2314     },
2315     onNodeDrop : function(n, dd, e, data)
2316     {
2317         return false;
2318         
2319         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2320                 return false;
2321         }
2322         var pt = this.getDropPoint(e, n, dd);
2323         var insertAt = (n == this.bodyEl.dom) ? this.items.length : n.nodeIndex;
2324         if (pt == "below") {
2325             insertAt++;
2326         }
2327         for (var i = 0; i < this.items.length; i++) {
2328             var r = this.items[i];
2329             //var dup = this.store.getById(r.id);
2330             if (dup && (dd != this.dragZone)) {
2331                     Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
2332             } else {
2333                 if (data.copy) {
2334                     this.store.insert(insertAt++, r.copy());
2335                 } else {
2336                     data.source.isDirtyFlag = true;
2337                     r.store.remove(r);
2338                     this.store.insert(insertAt++, r);
2339                 }
2340                 this.isDirtyFlag = true;
2341             }
2342         }
2343         this.dragZone.cachedTarget = null;
2344         return true;
2345     },
2346     
2347     /** Decide whether to drop above or below a View node. */
2348     getDropPoint : function(e, n, dd)
2349     {
2350         if (dd) {
2351             return false;
2352         }
2353         if (n == this.bodyEl.dom) {
2354                 return "above";
2355         }
2356         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2357         var c = t + (b - t) / 2;
2358         var y = Roo.lib.Event.getPageY(e);
2359         if(y <= c) {
2360                 return "above";
2361         }else{
2362                 return "below";
2363         }
2364     },
2365     onToggleCollapse : function(e)
2366     {
2367         if (this.collapsed) {
2368             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2369             this.el.select('.roo-collapsable').addClass('show');
2370             this.collapsed = false;
2371             return;
2372         }
2373         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2374         this.el.select('.roo-collapsable').removeClass('show');
2375         this.collapsed = true;
2376         
2377         
2378     },
2379     dropPlaceHolder: function (action, where_ar, data)
2380     {
2381         if (this.dropEl === false) {
2382             this.dropEl = new Roo.DomHelper.append(this.dom.bodyEl, {
2383                 cls : 'd-none'
2384             });
2385         }
2386         
2387         if (action == 'hide') {
2388             this.dropEl.removeClass(['d-none', 'd-block']);
2389             this.dropEl.addClass('d-none');
2390             return;
2391         }
2392         var cardel = where_ar[0].el.dom;
2393         var dropEl
2394         
2395         
2396         
2397         
2398         
2399     }
2400
2401     
2402 });
2403
2404 /*
2405  * - LGPL
2406  *
2407  * Card header - holder for the card header elements.
2408  * 
2409  */
2410
2411 /**
2412  * @class Roo.bootstrap.CardHeader
2413  * @extends Roo.bootstrap.Element
2414  * Bootstrap CardHeader class
2415  * @constructor
2416  * Create a new Card Header - that you can embed children into
2417  * @param {Object} config The config object
2418  */
2419
2420 Roo.bootstrap.CardHeader = function(config){
2421     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2422 };
2423
2424 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2425     
2426     
2427     container_method : 'getCardHeader' 
2428     
2429      
2430     
2431     
2432    
2433 });
2434
2435  
2436
2437  /*
2438  * - LGPL
2439  *
2440  * Card header - holder for the card header elements.
2441  * 
2442  */
2443
2444 /**
2445  * @class Roo.bootstrap.CardImageTop
2446  * @extends Roo.bootstrap.Element
2447  * Bootstrap CardImageTop class
2448  * @constructor
2449  * Create a new Card Image Top container
2450  * @param {Object} config The config object
2451  */
2452
2453 Roo.bootstrap.CardImageTop = function(config){
2454     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2455 };
2456
2457 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2458     
2459    
2460     container_method : 'getCardImageTop' 
2461     
2462      
2463     
2464    
2465 });
2466
2467  
2468
2469  /*
2470  * - LGPL
2471  *
2472  * image
2473  * 
2474  */
2475
2476
2477 /**
2478  * @class Roo.bootstrap.Img
2479  * @extends Roo.bootstrap.Component
2480  * Bootstrap Img class
2481  * @cfg {Boolean} imgResponsive false | true
2482  * @cfg {String} border rounded | circle | thumbnail
2483  * @cfg {String} src image source
2484  * @cfg {String} alt image alternative text
2485  * @cfg {String} href a tag href
2486  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2487  * @cfg {String} xsUrl xs image source
2488  * @cfg {String} smUrl sm image source
2489  * @cfg {String} mdUrl md image source
2490  * @cfg {String} lgUrl lg image source
2491  * 
2492  * @constructor
2493  * Create a new Input
2494  * @param {Object} config The config object
2495  */
2496
2497 Roo.bootstrap.Img = function(config){
2498     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2499     
2500     this.addEvents({
2501         // img events
2502         /**
2503          * @event click
2504          * The img click event for the img.
2505          * @param {Roo.EventObject} e
2506          */
2507         "click" : true
2508     });
2509 };
2510
2511 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2512     
2513     imgResponsive: true,
2514     border: '',
2515     src: 'about:blank',
2516     href: false,
2517     target: false,
2518     xsUrl: '',
2519     smUrl: '',
2520     mdUrl: '',
2521     lgUrl: '',
2522
2523     getAutoCreate : function()
2524     {   
2525         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2526             return this.createSingleImg();
2527         }
2528         
2529         var cfg = {
2530             tag: 'div',
2531             cls: 'roo-image-responsive-group',
2532             cn: []
2533         };
2534         var _this = this;
2535         
2536         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2537             
2538             if(!_this[size + 'Url']){
2539                 return;
2540             }
2541             
2542             var img = {
2543                 tag: 'img',
2544                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2545                 html: _this.html || cfg.html,
2546                 src: _this[size + 'Url']
2547             };
2548             
2549             img.cls += ' roo-image-responsive-' + size;
2550             
2551             var s = ['xs', 'sm', 'md', 'lg'];
2552             
2553             s.splice(s.indexOf(size), 1);
2554             
2555             Roo.each(s, function(ss){
2556                 img.cls += ' hidden-' + ss;
2557             });
2558             
2559             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2560                 cfg.cls += ' img-' + _this.border;
2561             }
2562             
2563             if(_this.alt){
2564                 cfg.alt = _this.alt;
2565             }
2566             
2567             if(_this.href){
2568                 var a = {
2569                     tag: 'a',
2570                     href: _this.href,
2571                     cn: [
2572                         img
2573                     ]
2574                 };
2575
2576                 if(this.target){
2577                     a.target = _this.target;
2578                 }
2579             }
2580             
2581             cfg.cn.push((_this.href) ? a : img);
2582             
2583         });
2584         
2585         return cfg;
2586     },
2587     
2588     createSingleImg : function()
2589     {
2590         var cfg = {
2591             tag: 'img',
2592             cls: (this.imgResponsive) ? 'img-responsive' : '',
2593             html : null,
2594             src : 'about:blank'  // just incase src get's set to undefined?!?
2595         };
2596         
2597         cfg.html = this.html || cfg.html;
2598         
2599         cfg.src = this.src || cfg.src;
2600         
2601         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2602             cfg.cls += ' img-' + this.border;
2603         }
2604         
2605         if(this.alt){
2606             cfg.alt = this.alt;
2607         }
2608         
2609         if(this.href){
2610             var a = {
2611                 tag: 'a',
2612                 href: this.href,
2613                 cn: [
2614                     cfg
2615                 ]
2616             };
2617             
2618             if(this.target){
2619                 a.target = this.target;
2620             }
2621             
2622         }
2623         
2624         return (this.href) ? a : cfg;
2625     },
2626     
2627     initEvents: function() 
2628     {
2629         if(!this.href){
2630             this.el.on('click', this.onClick, this);
2631         }
2632         
2633     },
2634     
2635     onClick : function(e)
2636     {
2637         Roo.log('img onclick');
2638         this.fireEvent('click', this, e);
2639     },
2640     /**
2641      * Sets the url of the image - used to update it
2642      * @param {String} url the url of the image
2643      */
2644     
2645     setSrc : function(url)
2646     {
2647         this.src =  url;
2648         
2649         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2650             this.el.dom.src =  url;
2651             return;
2652         }
2653         
2654         this.el.select('img', true).first().dom.src =  url;
2655     }
2656     
2657     
2658    
2659 });
2660
2661  /*
2662  * - LGPL
2663  *
2664  * image
2665  * 
2666  */
2667
2668
2669 /**
2670  * @class Roo.bootstrap.Link
2671  * @extends Roo.bootstrap.Component
2672  * Bootstrap Link Class
2673  * @cfg {String} alt image alternative text
2674  * @cfg {String} href a tag href
2675  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2676  * @cfg {String} html the content of the link.
2677  * @cfg {String} anchor name for the anchor link
2678  * @cfg {String} fa - favicon
2679
2680  * @cfg {Boolean} preventDefault (true | false) default false
2681
2682  * 
2683  * @constructor
2684  * Create a new Input
2685  * @param {Object} config The config object
2686  */
2687
2688 Roo.bootstrap.Link = function(config){
2689     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2690     
2691     this.addEvents({
2692         // img events
2693         /**
2694          * @event click
2695          * The img click event for the img.
2696          * @param {Roo.EventObject} e
2697          */
2698         "click" : true
2699     });
2700 };
2701
2702 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2703     
2704     href: false,
2705     target: false,
2706     preventDefault: false,
2707     anchor : false,
2708     alt : false,
2709     fa: false,
2710
2711
2712     getAutoCreate : function()
2713     {
2714         var html = this.html || '';
2715         
2716         if (this.fa !== false) {
2717             html = '<i class="fa fa-' + this.fa + '"></i>';
2718         }
2719         var cfg = {
2720             tag: 'a'
2721         };
2722         // anchor's do not require html/href...
2723         if (this.anchor === false) {
2724             cfg.html = html;
2725             cfg.href = this.href || '#';
2726         } else {
2727             cfg.name = this.anchor;
2728             if (this.html !== false || this.fa !== false) {
2729                 cfg.html = html;
2730             }
2731             if (this.href !== false) {
2732                 cfg.href = this.href;
2733             }
2734         }
2735         
2736         if(this.alt !== false){
2737             cfg.alt = this.alt;
2738         }
2739         
2740         
2741         if(this.target !== false) {
2742             cfg.target = this.target;
2743         }
2744         
2745         return cfg;
2746     },
2747     
2748     initEvents: function() {
2749         
2750         if(!this.href || this.preventDefault){
2751             this.el.on('click', this.onClick, this);
2752         }
2753     },
2754     
2755     onClick : function(e)
2756     {
2757         if(this.preventDefault){
2758             e.preventDefault();
2759         }
2760         //Roo.log('img onclick');
2761         this.fireEvent('click', this, e);
2762     }
2763    
2764 });
2765
2766  /*
2767  * - LGPL
2768  *
2769  * header
2770  * 
2771  */
2772
2773 /**
2774  * @class Roo.bootstrap.Header
2775  * @extends Roo.bootstrap.Component
2776  * Bootstrap Header class
2777  * @cfg {String} html content of header
2778  * @cfg {Number} level (1|2|3|4|5|6) default 1
2779  * 
2780  * @constructor
2781  * Create a new Header
2782  * @param {Object} config The config object
2783  */
2784
2785
2786 Roo.bootstrap.Header  = function(config){
2787     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2791     
2792     //href : false,
2793     html : false,
2794     level : 1,
2795     
2796     
2797     
2798     getAutoCreate : function(){
2799         
2800         
2801         
2802         var cfg = {
2803             tag: 'h' + (1 *this.level),
2804             html: this.html || ''
2805         } ;
2806         
2807         return cfg;
2808     }
2809    
2810 });
2811
2812  
2813
2814  /*
2815  * Based on:
2816  * Ext JS Library 1.1.1
2817  * Copyright(c) 2006-2007, Ext JS, LLC.
2818  *
2819  * Originally Released Under LGPL - original licence link has changed is not relivant.
2820  *
2821  * Fork - LGPL
2822  * <script type="text/javascript">
2823  */
2824  
2825 /**
2826  * @class Roo.bootstrap.MenuMgr
2827  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2828  * @singleton
2829  */
2830 Roo.bootstrap.MenuMgr = function(){
2831    var menus, active, groups = {}, attached = false, lastShow = new Date();
2832
2833    // private - called when first menu is created
2834    function init(){
2835        menus = {};
2836        active = new Roo.util.MixedCollection();
2837        Roo.get(document).addKeyListener(27, function(){
2838            if(active.length > 0){
2839                hideAll();
2840            }
2841        });
2842    }
2843
2844    // private
2845    function hideAll(){
2846        if(active && active.length > 0){
2847            var c = active.clone();
2848            c.each(function(m){
2849                m.hide();
2850            });
2851        }
2852    }
2853
2854    // private
2855    function onHide(m){
2856        active.remove(m);
2857        if(active.length < 1){
2858            Roo.get(document).un("mouseup", onMouseDown);
2859             
2860            attached = false;
2861        }
2862    }
2863
2864    // private
2865    function onShow(m){
2866        var last = active.last();
2867        lastShow = new Date();
2868        active.add(m);
2869        if(!attached){
2870           Roo.get(document).on("mouseup", onMouseDown);
2871            
2872            attached = true;
2873        }
2874        if(m.parentMenu){
2875           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2876           m.parentMenu.activeChild = m;
2877        }else if(last && last.isVisible()){
2878           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2879        }
2880    }
2881
2882    // private
2883    function onBeforeHide(m){
2884        if(m.activeChild){
2885            m.activeChild.hide();
2886        }
2887        if(m.autoHideTimer){
2888            clearTimeout(m.autoHideTimer);
2889            delete m.autoHideTimer;
2890        }
2891    }
2892
2893    // private
2894    function onBeforeShow(m){
2895        var pm = m.parentMenu;
2896        if(!pm && !m.allowOtherMenus){
2897            hideAll();
2898        }else if(pm && pm.activeChild && active != m){
2899            pm.activeChild.hide();
2900        }
2901    }
2902
2903    // private this should really trigger on mouseup..
2904    function onMouseDown(e){
2905         Roo.log("on Mouse Up");
2906         
2907         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2908             Roo.log("MenuManager hideAll");
2909             hideAll();
2910             e.stopEvent();
2911         }
2912         
2913         
2914    }
2915
2916    // private
2917    function onBeforeCheck(mi, state){
2918        if(state){
2919            var g = groups[mi.group];
2920            for(var i = 0, l = g.length; i < l; i++){
2921                if(g[i] != mi){
2922                    g[i].setChecked(false);
2923                }
2924            }
2925        }
2926    }
2927
2928    return {
2929
2930        /**
2931         * Hides all menus that are currently visible
2932         */
2933        hideAll : function(){
2934             hideAll();  
2935        },
2936
2937        // private
2938        register : function(menu){
2939            if(!menus){
2940                init();
2941            }
2942            menus[menu.id] = menu;
2943            menu.on("beforehide", onBeforeHide);
2944            menu.on("hide", onHide);
2945            menu.on("beforeshow", onBeforeShow);
2946            menu.on("show", onShow);
2947            var g = menu.group;
2948            if(g && menu.events["checkchange"]){
2949                if(!groups[g]){
2950                    groups[g] = [];
2951                }
2952                groups[g].push(menu);
2953                menu.on("checkchange", onCheck);
2954            }
2955        },
2956
2957         /**
2958          * Returns a {@link Roo.menu.Menu} object
2959          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2960          * be used to generate and return a new Menu instance.
2961          */
2962        get : function(menu){
2963            if(typeof menu == "string"){ // menu id
2964                return menus[menu];
2965            }else if(menu.events){  // menu instance
2966                return menu;
2967            }
2968            /*else if(typeof menu.length == 'number'){ // array of menu items?
2969                return new Roo.bootstrap.Menu({items:menu});
2970            }else{ // otherwise, must be a config
2971                return new Roo.bootstrap.Menu(menu);
2972            }
2973            */
2974            return false;
2975        },
2976
2977        // private
2978        unregister : function(menu){
2979            delete menus[menu.id];
2980            menu.un("beforehide", onBeforeHide);
2981            menu.un("hide", onHide);
2982            menu.un("beforeshow", onBeforeShow);
2983            menu.un("show", onShow);
2984            var g = menu.group;
2985            if(g && menu.events["checkchange"]){
2986                groups[g].remove(menu);
2987                menu.un("checkchange", onCheck);
2988            }
2989        },
2990
2991        // private
2992        registerCheckable : function(menuItem){
2993            var g = menuItem.group;
2994            if(g){
2995                if(!groups[g]){
2996                    groups[g] = [];
2997                }
2998                groups[g].push(menuItem);
2999                menuItem.on("beforecheckchange", onBeforeCheck);
3000            }
3001        },
3002
3003        // private
3004        unregisterCheckable : function(menuItem){
3005            var g = menuItem.group;
3006            if(g){
3007                groups[g].remove(menuItem);
3008                menuItem.un("beforecheckchange", onBeforeCheck);
3009            }
3010        }
3011    };
3012 }();/*
3013  * - LGPL
3014  *
3015  * menu
3016  * 
3017  */
3018
3019 /**
3020  * @class Roo.bootstrap.Menu
3021  * @extends Roo.bootstrap.Component
3022  * Bootstrap Menu class - container for MenuItems
3023  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3024  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3025  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3026  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3027  * 
3028  * @constructor
3029  * Create a new Menu
3030  * @param {Object} config The config object
3031  */
3032
3033
3034 Roo.bootstrap.Menu = function(config){
3035     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3036     if (this.registerMenu && this.type != 'treeview')  {
3037         Roo.bootstrap.MenuMgr.register(this);
3038     }
3039     
3040     
3041     this.addEvents({
3042         /**
3043          * @event beforeshow
3044          * Fires before this menu is displayed (return false to block)
3045          * @param {Roo.menu.Menu} this
3046          */
3047         beforeshow : true,
3048         /**
3049          * @event beforehide
3050          * Fires before this menu is hidden (return false to block)
3051          * @param {Roo.menu.Menu} this
3052          */
3053         beforehide : true,
3054         /**
3055          * @event show
3056          * Fires after this menu is displayed
3057          * @param {Roo.menu.Menu} this
3058          */
3059         show : true,
3060         /**
3061          * @event hide
3062          * Fires after this menu is hidden
3063          * @param {Roo.menu.Menu} this
3064          */
3065         hide : true,
3066         /**
3067          * @event click
3068          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3069          * @param {Roo.menu.Menu} this
3070          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3071          * @param {Roo.EventObject} e
3072          */
3073         click : true,
3074         /**
3075          * @event mouseover
3076          * Fires when the mouse is hovering over this menu
3077          * @param {Roo.menu.Menu} this
3078          * @param {Roo.EventObject} e
3079          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3080          */
3081         mouseover : true,
3082         /**
3083          * @event mouseout
3084          * Fires when the mouse exits this menu
3085          * @param {Roo.menu.Menu} this
3086          * @param {Roo.EventObject} e
3087          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3088          */
3089         mouseout : true,
3090         /**
3091          * @event itemclick
3092          * Fires when a menu item contained in this menu is clicked
3093          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3094          * @param {Roo.EventObject} e
3095          */
3096         itemclick: true
3097     });
3098     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3099 };
3100
3101 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3102     
3103    /// html : false,
3104     //align : '',
3105     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3106     type: false,
3107     /**
3108      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3109      */
3110     registerMenu : true,
3111     
3112     menuItems :false, // stores the menu items..
3113     
3114     hidden:true,
3115         
3116     parentMenu : false,
3117     
3118     stopEvent : true,
3119     
3120     isLink : false,
3121     
3122     getChildContainer : function() {
3123         return this.el;  
3124     },
3125     
3126     getAutoCreate : function(){
3127          
3128         //if (['right'].indexOf(this.align)!==-1) {
3129         //    cfg.cn[1].cls += ' pull-right'
3130         //}
3131         
3132         
3133         var cfg = {
3134             tag : 'ul',
3135             cls : 'dropdown-menu' ,
3136             style : 'z-index:1000'
3137             
3138         };
3139         
3140         if (this.type === 'submenu') {
3141             cfg.cls = 'submenu active';
3142         }
3143         if (this.type === 'treeview') {
3144             cfg.cls = 'treeview-menu';
3145         }
3146         
3147         return cfg;
3148     },
3149     initEvents : function() {
3150         
3151        // Roo.log("ADD event");
3152        // Roo.log(this.triggerEl.dom);
3153         
3154         this.triggerEl.on('click', this.onTriggerClick, this);
3155         
3156         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3157         
3158         
3159         if (this.triggerEl.hasClass('nav-item')) {
3160             // dropdown toggle on the 'a' in BS4?
3161             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3162         } else {
3163             this.triggerEl.addClass('dropdown-toggle');
3164         }
3165         if (Roo.isTouch) {
3166             this.el.on('touchstart'  , this.onTouch, this);
3167         }
3168         this.el.on('click' , this.onClick, this);
3169
3170         this.el.on("mouseover", this.onMouseOver, this);
3171         this.el.on("mouseout", this.onMouseOut, this);
3172         
3173     },
3174     
3175     findTargetItem : function(e)
3176     {
3177         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3178         if(!t){
3179             return false;
3180         }
3181         //Roo.log(t);         Roo.log(t.id);
3182         if(t && t.id){
3183             //Roo.log(this.menuitems);
3184             return this.menuitems.get(t.id);
3185             
3186             //return this.items.get(t.menuItemId);
3187         }
3188         
3189         return false;
3190     },
3191     
3192     onTouch : function(e) 
3193     {
3194         Roo.log("menu.onTouch");
3195         //e.stopEvent(); this make the user popdown broken
3196         this.onClick(e);
3197     },
3198     
3199     onClick : function(e)
3200     {
3201         Roo.log("menu.onClick");
3202         
3203         var t = this.findTargetItem(e);
3204         if(!t || t.isContainer){
3205             return;
3206         }
3207         Roo.log(e);
3208         /*
3209         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3210             if(t == this.activeItem && t.shouldDeactivate(e)){
3211                 this.activeItem.deactivate();
3212                 delete this.activeItem;
3213                 return;
3214             }
3215             if(t.canActivate){
3216                 this.setActiveItem(t, true);
3217             }
3218             return;
3219             
3220             
3221         }
3222         */
3223        
3224         Roo.log('pass click event');
3225         
3226         t.onClick(e);
3227         
3228         this.fireEvent("click", this, t, e);
3229         
3230         var _this = this;
3231         
3232         if(!t.href.length || t.href == '#'){
3233             (function() { _this.hide(); }).defer(100);
3234         }
3235         
3236     },
3237     
3238     onMouseOver : function(e){
3239         var t  = this.findTargetItem(e);
3240         //Roo.log(t);
3241         //if(t){
3242         //    if(t.canActivate && !t.disabled){
3243         //        this.setActiveItem(t, true);
3244         //    }
3245         //}
3246         
3247         this.fireEvent("mouseover", this, e, t);
3248     },
3249     isVisible : function(){
3250         return !this.hidden;
3251     },
3252     onMouseOut : function(e){
3253         var t  = this.findTargetItem(e);
3254         
3255         //if(t ){
3256         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3257         //        this.activeItem.deactivate();
3258         //        delete this.activeItem;
3259         //    }
3260         //}
3261         this.fireEvent("mouseout", this, e, t);
3262     },
3263     
3264     
3265     /**
3266      * Displays this menu relative to another element
3267      * @param {String/HTMLElement/Roo.Element} element The element to align to
3268      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3269      * the element (defaults to this.defaultAlign)
3270      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3271      */
3272     show : function(el, pos, parentMenu)
3273     {
3274         if (false === this.fireEvent("beforeshow", this)) {
3275             Roo.log("show canceled");
3276             return;
3277         }
3278         this.parentMenu = parentMenu;
3279         if(!this.el){
3280             this.render();
3281         }
3282         
3283         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3284     },
3285      /**
3286      * Displays this menu at a specific xy position
3287      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3289      */
3290     showAt : function(xy, parentMenu, /* private: */_e){
3291         this.parentMenu = parentMenu;
3292         if(!this.el){
3293             this.render();
3294         }
3295         if(_e !== false){
3296             this.fireEvent("beforeshow", this);
3297             //xy = this.el.adjustForConstraints(xy);
3298         }
3299         
3300         //this.el.show();
3301         this.hideMenuItems();
3302         this.hidden = false;
3303         this.triggerEl.addClass('open');
3304         this.el.addClass('show');
3305         
3306         // reassign x when hitting right
3307         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3308             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3309         }
3310         
3311         // reassign y when hitting bottom
3312         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3313             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3314         }
3315         
3316         // but the list may align on trigger left or trigger top... should it be a properity?
3317         
3318         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3319             this.el.setXY(xy);
3320         }
3321         
3322         this.focus();
3323         this.fireEvent("show", this);
3324     },
3325     
3326     focus : function(){
3327         return;
3328         if(!this.hidden){
3329             this.doFocus.defer(50, this);
3330         }
3331     },
3332
3333     doFocus : function(){
3334         if(!this.hidden){
3335             this.focusEl.focus();
3336         }
3337     },
3338
3339     /**
3340      * Hides this menu and optionally all parent menus
3341      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3342      */
3343     hide : function(deep)
3344     {
3345         if (false === this.fireEvent("beforehide", this)) {
3346             Roo.log("hide canceled");
3347             return;
3348         }
3349         this.hideMenuItems();
3350         if(this.el && this.isVisible()){
3351            
3352             if(this.activeItem){
3353                 this.activeItem.deactivate();
3354                 this.activeItem = null;
3355             }
3356             this.triggerEl.removeClass('open');;
3357             this.el.removeClass('show');
3358             this.hidden = true;
3359             this.fireEvent("hide", this);
3360         }
3361         if(deep === true && this.parentMenu){
3362             this.parentMenu.hide(true);
3363         }
3364     },
3365     
3366     onTriggerClick : function(e)
3367     {
3368         Roo.log('trigger click');
3369         
3370         var target = e.getTarget();
3371         
3372         Roo.log(target.nodeName.toLowerCase());
3373         
3374         if(target.nodeName.toLowerCase() === 'i'){
3375             e.preventDefault();
3376         }
3377         
3378     },
3379     
3380     onTriggerPress  : function(e)
3381     {
3382         Roo.log('trigger press');
3383         //Roo.log(e.getTarget());
3384        // Roo.log(this.triggerEl.dom);
3385        
3386         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3387         var pel = Roo.get(e.getTarget());
3388         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3389             Roo.log('is treeview or dropdown?');
3390             return;
3391         }
3392         
3393         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3394             return;
3395         }
3396         
3397         if (this.isVisible()) {
3398             Roo.log('hide');
3399             this.hide();
3400         } else {
3401             Roo.log('show');
3402             this.show(this.triggerEl, '?', false);
3403         }
3404         
3405         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3406             e.stopEvent();
3407         }
3408         
3409     },
3410        
3411     
3412     hideMenuItems : function()
3413     {
3414         Roo.log("hide Menu Items");
3415         if (!this.el) { 
3416             return;
3417         }
3418         
3419         this.el.select('.open',true).each(function(aa) {
3420             
3421             aa.removeClass('open');
3422          
3423         });
3424     },
3425     addxtypeChild : function (tree, cntr) {
3426         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3427           
3428         this.menuitems.add(comp);
3429         return comp;
3430
3431     },
3432     getEl : function()
3433     {
3434         Roo.log(this.el);
3435         return this.el;
3436     },
3437     
3438     clear : function()
3439     {
3440         this.getEl().dom.innerHTML = '';
3441         this.menuitems.clear();
3442     }
3443 });
3444
3445  
3446  /*
3447  * - LGPL
3448  *
3449  * menu item
3450  * 
3451  */
3452
3453
3454 /**
3455  * @class Roo.bootstrap.MenuItem
3456  * @extends Roo.bootstrap.Component
3457  * Bootstrap MenuItem class
3458  * @cfg {String} html the menu label
3459  * @cfg {String} href the link
3460  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3461  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3462  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3463  * @cfg {String} fa favicon to show on left of menu item.
3464  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3465  * 
3466  * 
3467  * @constructor
3468  * Create a new MenuItem
3469  * @param {Object} config The config object
3470  */
3471
3472
3473 Roo.bootstrap.MenuItem = function(config){
3474     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3475     this.addEvents({
3476         // raw events
3477         /**
3478          * @event click
3479          * The raw click event for the entire grid.
3480          * @param {Roo.bootstrap.MenuItem} this
3481          * @param {Roo.EventObject} e
3482          */
3483         "click" : true
3484     });
3485 };
3486
3487 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3488     
3489     href : false,
3490     html : false,
3491     preventDefault: false,
3492     isContainer : false,
3493     active : false,
3494     fa: false,
3495     
3496     getAutoCreate : function(){
3497         
3498         if(this.isContainer){
3499             return {
3500                 tag: 'li',
3501                 cls: 'dropdown-menu-item '
3502             };
3503         }
3504         var ctag = {
3505             tag: 'span',
3506             html: 'Link'
3507         };
3508         
3509         var anc = {
3510             tag : 'a',
3511             cls : 'dropdown-item',
3512             href : '#',
3513             cn : [  ]
3514         };
3515         
3516         if (this.fa !== false) {
3517             anc.cn.push({
3518                 tag : 'i',
3519                 cls : 'fa fa-' + this.fa
3520             });
3521         }
3522         
3523         anc.cn.push(ctag);
3524         
3525         
3526         var cfg= {
3527             tag: 'li',
3528             cls: 'dropdown-menu-item',
3529             cn: [ anc ]
3530         };
3531         if (this.parent().type == 'treeview') {
3532             cfg.cls = 'treeview-menu';
3533         }
3534         if (this.active) {
3535             cfg.cls += ' active';
3536         }
3537         
3538         
3539         
3540         anc.href = this.href || cfg.cn[0].href ;
3541         ctag.html = this.html || cfg.cn[0].html ;
3542         return cfg;
3543     },
3544     
3545     initEvents: function()
3546     {
3547         if (this.parent().type == 'treeview') {
3548             this.el.select('a').on('click', this.onClick, this);
3549         }
3550         
3551         if (this.menu) {
3552             this.menu.parentType = this.xtype;
3553             this.menu.triggerEl = this.el;
3554             this.menu = this.addxtype(Roo.apply({}, this.menu));
3555         }
3556         
3557     },
3558     onClick : function(e)
3559     {
3560         Roo.log('item on click ');
3561         
3562         if(this.preventDefault){
3563             e.preventDefault();
3564         }
3565         //this.parent().hideMenuItems();
3566         
3567         this.fireEvent('click', this, e);
3568     },
3569     getEl : function()
3570     {
3571         return this.el;
3572     } 
3573 });
3574
3575  
3576
3577  /*
3578  * - LGPL
3579  *
3580  * menu separator
3581  * 
3582  */
3583
3584
3585 /**
3586  * @class Roo.bootstrap.MenuSeparator
3587  * @extends Roo.bootstrap.Component
3588  * Bootstrap MenuSeparator class
3589  * 
3590  * @constructor
3591  * Create a new MenuItem
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.MenuSeparator = function(config){
3597     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3601     
3602     getAutoCreate : function(){
3603         var cfg = {
3604             cls: 'divider',
3605             tag : 'li'
3606         };
3607         
3608         return cfg;
3609     }
3610    
3611 });
3612
3613  
3614
3615  
3616 /*
3617 * Licence: LGPL
3618 */
3619
3620 /**
3621  * @class Roo.bootstrap.Modal
3622  * @extends Roo.bootstrap.Component
3623  * Bootstrap Modal class
3624  * @cfg {String} title Title of dialog
3625  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3626  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3627  * @cfg {Boolean} specificTitle default false
3628  * @cfg {Array} buttons Array of buttons or standard button set..
3629  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3630  * @cfg {Boolean} animate default true
3631  * @cfg {Boolean} allow_close default true
3632  * @cfg {Boolean} fitwindow default false
3633  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3634  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3635  * @cfg {String} size (sm|lg) default empty
3636  * @cfg {Number} max_width set the max width of modal
3637  *
3638  *
3639  * @constructor
3640  * Create a new Modal Dialog
3641  * @param {Object} config The config object
3642  */
3643
3644 Roo.bootstrap.Modal = function(config){
3645     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3646     this.addEvents({
3647         // raw events
3648         /**
3649          * @event btnclick
3650          * The raw btnclick event for the button
3651          * @param {Roo.EventObject} e
3652          */
3653         "btnclick" : true,
3654         /**
3655          * @event resize
3656          * Fire when dialog resize
3657          * @param {Roo.bootstrap.Modal} this
3658          * @param {Roo.EventObject} e
3659          */
3660         "resize" : true
3661     });
3662     this.buttons = this.buttons || [];
3663
3664     if (this.tmpl) {
3665         this.tmpl = Roo.factory(this.tmpl);
3666     }
3667
3668 };
3669
3670 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3671
3672     title : 'test dialog',
3673
3674     buttons : false,
3675
3676     // set on load...
3677
3678     html: false,
3679
3680     tmp: false,
3681
3682     specificTitle: false,
3683
3684     buttonPosition: 'right',
3685
3686     allow_close : true,
3687
3688     animate : true,
3689
3690     fitwindow: false,
3691     
3692      // private
3693     dialogEl: false,
3694     bodyEl:  false,
3695     footerEl:  false,
3696     titleEl:  false,
3697     closeEl:  false,
3698
3699     size: '',
3700     
3701     max_width: 0,
3702     
3703     max_height: 0,
3704     
3705     fit_content: false,
3706
3707     onRender : function(ct, position)
3708     {
3709         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3710
3711         if(!this.el){
3712             var cfg = Roo.apply({},  this.getAutoCreate());
3713             cfg.id = Roo.id();
3714             //if(!cfg.name){
3715             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3716             //}
3717             //if (!cfg.name.length) {
3718             //    delete cfg.name;
3719            // }
3720             if (this.cls) {
3721                 cfg.cls += ' ' + this.cls;
3722             }
3723             if (this.style) {
3724                 cfg.style = this.style;
3725             }
3726             this.el = Roo.get(document.body).createChild(cfg, position);
3727         }
3728         //var type = this.el.dom.type;
3729
3730
3731         if(this.tabIndex !== undefined){
3732             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3733         }
3734
3735         this.dialogEl = this.el.select('.modal-dialog',true).first();
3736         this.bodyEl = this.el.select('.modal-body',true).first();
3737         this.closeEl = this.el.select('.modal-header .close', true).first();
3738         this.headerEl = this.el.select('.modal-header',true).first();
3739         this.titleEl = this.el.select('.modal-title',true).first();
3740         this.footerEl = this.el.select('.modal-footer',true).first();
3741
3742         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3743         
3744         //this.el.addClass("x-dlg-modal");
3745
3746         if (this.buttons.length) {
3747             Roo.each(this.buttons, function(bb) {
3748                 var b = Roo.apply({}, bb);
3749                 b.xns = b.xns || Roo.bootstrap;
3750                 b.xtype = b.xtype || 'Button';
3751                 if (typeof(b.listeners) == 'undefined') {
3752                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3753                 }
3754
3755                 var btn = Roo.factory(b);
3756
3757                 btn.render(this.getButtonContainer());
3758
3759             },this);
3760         }
3761         // render the children.
3762         var nitems = [];
3763
3764         if(typeof(this.items) != 'undefined'){
3765             var items = this.items;
3766             delete this.items;
3767
3768             for(var i =0;i < items.length;i++) {
3769                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3770             }
3771         }
3772
3773         this.items = nitems;
3774
3775         // where are these used - they used to be body/close/footer
3776
3777
3778         this.initEvents();
3779         //this.el.addClass([this.fieldClass, this.cls]);
3780
3781     },
3782
3783     getAutoCreate : function()
3784     {
3785         // we will default to modal-body-overflow - might need to remove or make optional later.
3786         var bdy = {
3787                 cls : 'modal-body enable-modal-body-overflow ', 
3788                 html : this.html || ''
3789         };
3790
3791         var title = {
3792             tag: 'h4',
3793             cls : 'modal-title',
3794             html : this.title
3795         };
3796
3797         if(this.specificTitle){
3798             title = this.title;
3799
3800         }
3801
3802         var header = [];
3803         if (this.allow_close && Roo.bootstrap.version == 3) {
3804             header.push({
3805                 tag: 'button',
3806                 cls : 'close',
3807                 html : '&times'
3808             });
3809         }
3810
3811         header.push(title);
3812
3813         if (this.allow_close && Roo.bootstrap.version == 4) {
3814             header.push({
3815                 tag: 'button',
3816                 cls : 'close',
3817                 html : '&times'
3818             });
3819         }
3820         
3821         var size = '';
3822
3823         if(this.size.length){
3824             size = 'modal-' + this.size;
3825         }
3826         
3827         var footer = Roo.bootstrap.version == 3 ?
3828             {
3829                 cls : 'modal-footer',
3830                 cn : [
3831                     {
3832                         tag: 'div',
3833                         cls: 'btn-' + this.buttonPosition
3834                     }
3835                 ]
3836
3837             } :
3838             {  // BS4 uses mr-auto on left buttons....
3839                 cls : 'modal-footer'
3840             };
3841
3842             
3843
3844         
3845         
3846         var modal = {
3847             cls: "modal",
3848              cn : [
3849                 {
3850                     cls: "modal-dialog " + size,
3851                     cn : [
3852                         {
3853                             cls : "modal-content",
3854                             cn : [
3855                                 {
3856                                     cls : 'modal-header',
3857                                     cn : header
3858                                 },
3859                                 bdy,
3860                                 footer
3861                             ]
3862
3863                         }
3864                     ]
3865
3866                 }
3867             ]
3868         };
3869
3870         if(this.animate){
3871             modal.cls += ' fade';
3872         }
3873
3874         return modal;
3875
3876     },
3877     getChildContainer : function() {
3878
3879          return this.bodyEl;
3880
3881     },
3882     getButtonContainer : function() {
3883         
3884          return Roo.bootstrap.version == 4 ?
3885             this.el.select('.modal-footer',true).first()
3886             : this.el.select('.modal-footer div',true).first();
3887
3888     },
3889     initEvents : function()
3890     {
3891         if (this.allow_close) {
3892             this.closeEl.on('click', this.hide, this);
3893         }
3894         Roo.EventManager.onWindowResize(this.resize, this, true);
3895
3896
3897     },
3898   
3899
3900     resize : function()
3901     {
3902         this.maskEl.setSize(
3903             Roo.lib.Dom.getViewWidth(true),
3904             Roo.lib.Dom.getViewHeight(true)
3905         );
3906         
3907         if (this.fitwindow) {
3908             
3909            
3910             this.setSize(
3911                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3912                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3913             );
3914             return;
3915         }
3916         
3917         if(this.max_width !== 0) {
3918             
3919             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3920             
3921             if(this.height) {
3922                 this.setSize(w, this.height);
3923                 return;
3924             }
3925             
3926             if(this.max_height) {
3927                 this.setSize(w,Math.min(
3928                     this.max_height,
3929                     Roo.lib.Dom.getViewportHeight(true) - 60
3930                 ));
3931                 
3932                 return;
3933             }
3934             
3935             if(!this.fit_content) {
3936                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3937                 return;
3938             }
3939             
3940             this.setSize(w, Math.min(
3941                 60 +
3942                 this.headerEl.getHeight() + 
3943                 this.footerEl.getHeight() + 
3944                 this.getChildHeight(this.bodyEl.dom.childNodes),
3945                 Roo.lib.Dom.getViewportHeight(true) - 60)
3946             );
3947         }
3948         
3949     },
3950
3951     setSize : function(w,h)
3952     {
3953         if (!w && !h) {
3954             return;
3955         }
3956         
3957         this.resizeTo(w,h);
3958     },
3959
3960     show : function() {
3961
3962         if (!this.rendered) {
3963             this.render();
3964         }
3965
3966         //this.el.setStyle('display', 'block');
3967         this.el.removeClass('hideing');
3968         this.el.dom.style.display='block';
3969         
3970         Roo.get(document.body).addClass('modal-open');
3971  
3972         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3973             
3974             (function(){
3975                 this.el.addClass('show');
3976                 this.el.addClass('in');
3977             }).defer(50, this);
3978         }else{
3979             this.el.addClass('show');
3980             this.el.addClass('in');
3981         }
3982
3983         // not sure how we can show data in here..
3984         //if (this.tmpl) {
3985         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3986         //}
3987
3988         Roo.get(document.body).addClass("x-body-masked");
3989         
3990         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3991         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3992         this.maskEl.dom.style.display = 'block';
3993         this.maskEl.addClass('show');
3994         
3995         
3996         this.resize();
3997         
3998         this.fireEvent('show', this);
3999
4000         // set zindex here - otherwise it appears to be ignored...
4001         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4002
4003         (function () {
4004             this.items.forEach( function(e) {
4005                 e.layout ? e.layout() : false;
4006
4007             });
4008         }).defer(100,this);
4009
4010     },
4011     hide : function()
4012     {
4013         if(this.fireEvent("beforehide", this) !== false){
4014             
4015             this.maskEl.removeClass('show');
4016             
4017             this.maskEl.dom.style.display = '';
4018             Roo.get(document.body).removeClass("x-body-masked");
4019             this.el.removeClass('in');
4020             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4021
4022             if(this.animate){ // why
4023                 this.el.addClass('hideing');
4024                 this.el.removeClass('show');
4025                 (function(){
4026                     if (!this.el.hasClass('hideing')) {
4027                         return; // it's been shown again...
4028                     }
4029                     
4030                     this.el.dom.style.display='';
4031
4032                     Roo.get(document.body).removeClass('modal-open');
4033                     this.el.removeClass('hideing');
4034                 }).defer(150,this);
4035                 
4036             }else{
4037                 this.el.removeClass('show');
4038                 this.el.dom.style.display='';
4039                 Roo.get(document.body).removeClass('modal-open');
4040
4041             }
4042             this.fireEvent('hide', this);
4043         }
4044     },
4045     isVisible : function()
4046     {
4047         
4048         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4049         
4050     },
4051
4052     addButton : function(str, cb)
4053     {
4054
4055
4056         var b = Roo.apply({}, { html : str } );
4057         b.xns = b.xns || Roo.bootstrap;
4058         b.xtype = b.xtype || 'Button';
4059         if (typeof(b.listeners) == 'undefined') {
4060             b.listeners = { click : cb.createDelegate(this)  };
4061         }
4062
4063         var btn = Roo.factory(b);
4064
4065         btn.render(this.getButtonContainer());
4066
4067         return btn;
4068
4069     },
4070
4071     setDefaultButton : function(btn)
4072     {
4073         //this.el.select('.modal-footer').()
4074     },
4075
4076     resizeTo: function(w,h)
4077     {
4078         this.dialogEl.setWidth(w);
4079         
4080         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4081
4082         this.bodyEl.setHeight(h - diff);
4083         
4084         this.fireEvent('resize', this);
4085     },
4086     
4087     setContentSize  : function(w, h)
4088     {
4089
4090     },
4091     onButtonClick: function(btn,e)
4092     {
4093         //Roo.log([a,b,c]);
4094         this.fireEvent('btnclick', btn.name, e);
4095     },
4096      /**
4097      * Set the title of the Dialog
4098      * @param {String} str new Title
4099      */
4100     setTitle: function(str) {
4101         this.titleEl.dom.innerHTML = str;
4102     },
4103     /**
4104      * Set the body of the Dialog
4105      * @param {String} str new Title
4106      */
4107     setBody: function(str) {
4108         this.bodyEl.dom.innerHTML = str;
4109     },
4110     /**
4111      * Set the body of the Dialog using the template
4112      * @param {Obj} data - apply this data to the template and replace the body contents.
4113      */
4114     applyBody: function(obj)
4115     {
4116         if (!this.tmpl) {
4117             Roo.log("Error - using apply Body without a template");
4118             //code
4119         }
4120         this.tmpl.overwrite(this.bodyEl, obj);
4121     },
4122     
4123     getChildHeight : function(child_nodes)
4124     {
4125         if(
4126             !child_nodes ||
4127             child_nodes.length == 0
4128         ) {
4129             return;
4130         }
4131         
4132         var child_height = 0;
4133         
4134         for(var i = 0; i < child_nodes.length; i++) {
4135             
4136             /*
4137             * for modal with tabs...
4138             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4139                 
4140                 var layout_childs = child_nodes[i].childNodes;
4141                 
4142                 for(var j = 0; j < layout_childs.length; j++) {
4143                     
4144                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4145                         
4146                         var layout_body_childs = layout_childs[j].childNodes;
4147                         
4148                         for(var k = 0; k < layout_body_childs.length; k++) {
4149                             
4150                             if(layout_body_childs[k].classList.contains('navbar')) {
4151                                 child_height += layout_body_childs[k].offsetHeight;
4152                                 continue;
4153                             }
4154                             
4155                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4156                                 
4157                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4158                                 
4159                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4160                                     
4161                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4162                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4163                                         continue;
4164                                     }
4165                                     
4166                                 }
4167                                 
4168                             }
4169                             
4170                         }
4171                     }
4172                 }
4173                 continue;
4174             }
4175             */
4176             
4177             child_height += child_nodes[i].offsetHeight;
4178             // Roo.log(child_nodes[i].offsetHeight);
4179         }
4180         
4181         return child_height;
4182     }
4183
4184 });
4185
4186
4187 Roo.apply(Roo.bootstrap.Modal,  {
4188     /**
4189          * Button config that displays a single OK button
4190          * @type Object
4191          */
4192         OK :  [{
4193             name : 'ok',
4194             weight : 'primary',
4195             html : 'OK'
4196         }],
4197         /**
4198          * Button config that displays Yes and No buttons
4199          * @type Object
4200          */
4201         YESNO : [
4202             {
4203                 name  : 'no',
4204                 html : 'No'
4205             },
4206             {
4207                 name  :'yes',
4208                 weight : 'primary',
4209                 html : 'Yes'
4210             }
4211         ],
4212
4213         /**
4214          * Button config that displays OK and Cancel buttons
4215          * @type Object
4216          */
4217         OKCANCEL : [
4218             {
4219                name : 'cancel',
4220                 html : 'Cancel'
4221             },
4222             {
4223                 name : 'ok',
4224                 weight : 'primary',
4225                 html : 'OK'
4226             }
4227         ],
4228         /**
4229          * Button config that displays Yes, No and Cancel buttons
4230          * @type Object
4231          */
4232         YESNOCANCEL : [
4233             {
4234                 name : 'yes',
4235                 weight : 'primary',
4236                 html : 'Yes'
4237             },
4238             {
4239                 name : 'no',
4240                 html : 'No'
4241             },
4242             {
4243                 name : 'cancel',
4244                 html : 'Cancel'
4245             }
4246         ],
4247         
4248         zIndex : 10001
4249 });
4250 /*
4251  * - LGPL
4252  *
4253  * messagebox - can be used as a replace
4254  * 
4255  */
4256 /**
4257  * @class Roo.MessageBox
4258  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4259  * Example usage:
4260  *<pre><code>
4261 // Basic alert:
4262 Roo.Msg.alert('Status', 'Changes saved successfully.');
4263
4264 // Prompt for user data:
4265 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4266     if (btn == 'ok'){
4267         // process text value...
4268     }
4269 });
4270
4271 // Show a dialog using config options:
4272 Roo.Msg.show({
4273    title:'Save Changes?',
4274    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4275    buttons: Roo.Msg.YESNOCANCEL,
4276    fn: processResult,
4277    animEl: 'elId'
4278 });
4279 </code></pre>
4280  * @singleton
4281  */
4282 Roo.bootstrap.MessageBox = function(){
4283     var dlg, opt, mask, waitTimer;
4284     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4285     var buttons, activeTextEl, bwidth;
4286
4287     
4288     // private
4289     var handleButton = function(button){
4290         dlg.hide();
4291         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4292     };
4293
4294     // private
4295     var handleHide = function(){
4296         if(opt && opt.cls){
4297             dlg.el.removeClass(opt.cls);
4298         }
4299         //if(waitTimer){
4300         //    Roo.TaskMgr.stop(waitTimer);
4301         //    waitTimer = null;
4302         //}
4303     };
4304
4305     // private
4306     var updateButtons = function(b){
4307         var width = 0;
4308         if(!b){
4309             buttons["ok"].hide();
4310             buttons["cancel"].hide();
4311             buttons["yes"].hide();
4312             buttons["no"].hide();
4313             dlg.footerEl.hide();
4314             
4315             return width;
4316         }
4317         dlg.footerEl.show();
4318         for(var k in buttons){
4319             if(typeof buttons[k] != "function"){
4320                 if(b[k]){
4321                     buttons[k].show();
4322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4323                     width += buttons[k].el.getWidth()+15;
4324                 }else{
4325                     buttons[k].hide();
4326                 }
4327             }
4328         }
4329         return width;
4330     };
4331
4332     // private
4333     var handleEsc = function(d, k, e){
4334         if(opt && opt.closable !== false){
4335             dlg.hide();
4336         }
4337         if(e){
4338             e.stopEvent();
4339         }
4340     };
4341
4342     return {
4343         /**
4344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4345          * @return {Roo.BasicDialog} The BasicDialog element
4346          */
4347         getDialog : function(){
4348            if(!dlg){
4349                 dlg = new Roo.bootstrap.Modal( {
4350                     //draggable: true,
4351                     //resizable:false,
4352                     //constraintoviewport:false,
4353                     //fixedcenter:true,
4354                     //collapsible : false,
4355                     //shim:true,
4356                     //modal: true,
4357                 //    width: 'auto',
4358                   //  height:100,
4359                     //buttonAlign:"center",
4360                     closeClick : function(){
4361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4362                             handleButton("no");
4363                         }else{
4364                             handleButton("cancel");
4365                         }
4366                     }
4367                 });
4368                 dlg.render();
4369                 dlg.on("hide", handleHide);
4370                 mask = dlg.mask;
4371                 //dlg.addKeyListener(27, handleEsc);
4372                 buttons = {};
4373                 this.buttons = buttons;
4374                 var bt = this.buttonText;
4375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4379                 //Roo.log(buttons);
4380                 bodyEl = dlg.bodyEl.createChild({
4381
4382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4383                         '<textarea class="roo-mb-textarea"></textarea>' +
4384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4385                 });
4386                 msgEl = bodyEl.dom.firstChild;
4387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4388                 textboxEl.enableDisplayMode();
4389                 textboxEl.addKeyListener([10,13], function(){
4390                     if(dlg.isVisible() && opt && opt.buttons){
4391                         if(opt.buttons.ok){
4392                             handleButton("ok");
4393                         }else if(opt.buttons.yes){
4394                             handleButton("yes");
4395                         }
4396                     }
4397                 });
4398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4399                 textareaEl.enableDisplayMode();
4400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4401                 progressEl.enableDisplayMode();
4402                 
4403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4404                 var pf = progressEl.dom.firstChild;
4405                 if (pf) {
4406                     pp = Roo.get(pf.firstChild);
4407                     pp.setHeight(pf.offsetHeight);
4408                 }
4409                 
4410             }
4411             return dlg;
4412         },
4413
4414         /**
4415          * Updates the message box body text
4416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4417          * the XHTML-compliant non-breaking space character '&amp;#160;')
4418          * @return {Roo.MessageBox} This message box
4419          */
4420         updateText : function(text)
4421         {
4422             if(!dlg.isVisible() && !opt.width){
4423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4425             }
4426             msgEl.innerHTML = text || '&#160;';
4427       
4428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4430             var w = Math.max(
4431                     Math.min(opt.width || cw , this.maxWidth), 
4432                     Math.max(opt.minWidth || this.minWidth, bwidth)
4433             );
4434             if(opt.prompt){
4435                 activeTextEl.setWidth(w);
4436             }
4437             if(dlg.isVisible()){
4438                 dlg.fixedcenter = false;
4439             }
4440             // to big, make it scroll. = But as usual stupid IE does not support
4441             // !important..
4442             
4443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4446             } else {
4447                 bodyEl.dom.style.height = '';
4448                 bodyEl.dom.style.overflowY = '';
4449             }
4450             if (cw > w) {
4451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4452             } else {
4453                 bodyEl.dom.style.overflowX = '';
4454             }
4455             
4456             dlg.setContentSize(w, bodyEl.getHeight());
4457             if(dlg.isVisible()){
4458                 dlg.fixedcenter = true;
4459             }
4460             return this;
4461         },
4462
4463         /**
4464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4468          * @return {Roo.MessageBox} This message box
4469          */
4470         updateProgress : function(value, text){
4471             if(text){
4472                 this.updateText(text);
4473             }
4474             
4475             if (pp) { // weird bug on my firefox - for some reason this is not defined
4476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4478             }
4479             return this;
4480         },        
4481
4482         /**
4483          * Returns true if the message box is currently displayed
4484          * @return {Boolean} True if the message box is visible, else false
4485          */
4486         isVisible : function(){
4487             return dlg && dlg.isVisible();  
4488         },
4489
4490         /**
4491          * Hides the message box if it is displayed
4492          */
4493         hide : function(){
4494             if(this.isVisible()){
4495                 dlg.hide();
4496             }  
4497         },
4498
4499         /**
4500          * Displays a new message box, or reinitializes an existing message box, based on the config options
4501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4502          * The following config object properties are supported:
4503          * <pre>
4504 Property    Type             Description
4505 ----------  ---------------  ------------------------------------------------------------------------------------
4506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4507                                    closes (defaults to undefined)
4508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4511                                    progress and wait dialogs will ignore this property and always hide the
4512                                    close button as they can only be closed programmatically.
4513 cls               String           A custom CSS class to apply to the message box element
4514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4515                                    displayed (defaults to 75)
4516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4517                                    function will be btn (the name of the button that was clicked, if applicable,
4518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4519                                    Progress and wait dialogs will ignore this option since they do not respond to
4520                                    user actions and can only be closed programmatically, so any required function
4521                                    should be called by the same code after it closes the dialog.
4522 icon              String           A CSS class that provides a background image to be used as an icon for
4523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4526 modal             Boolean          False to allow user interaction with the page while the message box is
4527                                    displayed (defaults to true)
4528 msg               String           A string that will replace the existing message box body text (defaults
4529                                    to the XHTML-compliant non-breaking space character '&#160;')
4530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4531 progress          Boolean          True to display a progress bar (defaults to false)
4532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4535 title             String           The title text
4536 value             String           The string value to set into the active textbox element if displayed
4537 wait              Boolean          True to display a progress bar (defaults to false)
4538 width             Number           The width of the dialog in pixels
4539 </pre>
4540          *
4541          * Example usage:
4542          * <pre><code>
4543 Roo.Msg.show({
4544    title: 'Address',
4545    msg: 'Please enter your address:',
4546    width: 300,
4547    buttons: Roo.MessageBox.OKCANCEL,
4548    multiline: true,
4549    fn: saveAddress,
4550    animEl: 'addAddressBtn'
4551 });
4552 </code></pre>
4553          * @param {Object} config Configuration options
4554          * @return {Roo.MessageBox} This message box
4555          */
4556         show : function(options)
4557         {
4558             
4559             // this causes nightmares if you show one dialog after another
4560             // especially on callbacks..
4561              
4562             if(this.isVisible()){
4563                 
4564                 this.hide();
4565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4567                 Roo.log("New Dialog Message:" +  options.msg )
4568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4570                 
4571             }
4572             var d = this.getDialog();
4573             opt = options;
4574             d.setTitle(opt.title || "&#160;");
4575             d.closeEl.setDisplayed(opt.closable !== false);
4576             activeTextEl = textboxEl;
4577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4578             if(opt.prompt){
4579                 if(opt.multiline){
4580                     textboxEl.hide();
4581                     textareaEl.show();
4582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4583                         opt.multiline : this.defaultTextHeight);
4584                     activeTextEl = textareaEl;
4585                 }else{
4586                     textboxEl.show();
4587                     textareaEl.hide();
4588                 }
4589             }else{
4590                 textboxEl.hide();
4591                 textareaEl.hide();
4592             }
4593             progressEl.setDisplayed(opt.progress === true);
4594             if (opt.progress) {
4595                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4596             }
4597             this.updateProgress(0);
4598             activeTextEl.dom.value = opt.value || "";
4599             if(opt.prompt){
4600                 dlg.setDefaultButton(activeTextEl);
4601             }else{
4602                 var bs = opt.buttons;
4603                 var db = null;
4604                 if(bs && bs.ok){
4605                     db = buttons["ok"];
4606                 }else if(bs && bs.yes){
4607                     db = buttons["yes"];
4608                 }
4609                 dlg.setDefaultButton(db);
4610             }
4611             bwidth = updateButtons(opt.buttons);
4612             this.updateText(opt.msg);
4613             if(opt.cls){
4614                 d.el.addClass(opt.cls);
4615             }
4616             d.proxyDrag = opt.proxyDrag === true;
4617             d.modal = opt.modal !== false;
4618             d.mask = opt.modal !== false ? mask : false;
4619             if(!d.isVisible()){
4620                 // force it to the end of the z-index stack so it gets a cursor in FF
4621                 document.body.appendChild(dlg.el.dom);
4622                 d.animateTarget = null;
4623                 d.show(options.animEl);
4624             }
4625             return this;
4626         },
4627
4628         /**
4629          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4630          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4631          * and closing the message box when the process is complete.
4632          * @param {String} title The title bar text
4633          * @param {String} msg The message box body text
4634          * @return {Roo.MessageBox} This message box
4635          */
4636         progress : function(title, msg){
4637             this.show({
4638                 title : title,
4639                 msg : msg,
4640                 buttons: false,
4641                 progress:true,
4642                 closable:false,
4643                 minWidth: this.minProgressWidth,
4644                 modal : true
4645             });
4646             return this;
4647         },
4648
4649         /**
4650          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4651          * If a callback function is passed it will be called after the user clicks the button, and the
4652          * id of the button that was clicked will be passed as the only parameter to the callback
4653          * (could also be the top-right close button).
4654          * @param {String} title The title bar text
4655          * @param {String} msg The message box body text
4656          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4657          * @param {Object} scope (optional) The scope of the callback function
4658          * @return {Roo.MessageBox} This message box
4659          */
4660         alert : function(title, msg, fn, scope)
4661         {
4662             this.show({
4663                 title : title,
4664                 msg : msg,
4665                 buttons: this.OK,
4666                 fn: fn,
4667                 closable : false,
4668                 scope : scope,
4669                 modal : true
4670             });
4671             return this;
4672         },
4673
4674         /**
4675          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4676          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4677          * You are responsible for closing the message box when the process is complete.
4678          * @param {String} msg The message box body text
4679          * @param {String} title (optional) The title bar text
4680          * @return {Roo.MessageBox} This message box
4681          */
4682         wait : function(msg, title){
4683             this.show({
4684                 title : title,
4685                 msg : msg,
4686                 buttons: false,
4687                 closable:false,
4688                 progress:true,
4689                 modal:true,
4690                 width:300,
4691                 wait:true
4692             });
4693             waitTimer = Roo.TaskMgr.start({
4694                 run: function(i){
4695                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4696                 },
4697                 interval: 1000
4698             });
4699             return this;
4700         },
4701
4702         /**
4703          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4704          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4705          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4706          * @param {String} title The title bar text
4707          * @param {String} msg The message box body text
4708          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4709          * @param {Object} scope (optional) The scope of the callback function
4710          * @return {Roo.MessageBox} This message box
4711          */
4712         confirm : function(title, msg, fn, scope){
4713             this.show({
4714                 title : title,
4715                 msg : msg,
4716                 buttons: this.YESNO,
4717                 fn: fn,
4718                 scope : scope,
4719                 modal : true
4720             });
4721             return this;
4722         },
4723
4724         /**
4725          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4726          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4727          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4728          * (could also be the top-right close button) and the text that was entered will be passed as the two
4729          * parameters to the callback.
4730          * @param {String} title The title bar text
4731          * @param {String} msg The message box body text
4732          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4733          * @param {Object} scope (optional) The scope of the callback function
4734          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4735          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4736          * @return {Roo.MessageBox} This message box
4737          */
4738         prompt : function(title, msg, fn, scope, multiline){
4739             this.show({
4740                 title : title,
4741                 msg : msg,
4742                 buttons: this.OKCANCEL,
4743                 fn: fn,
4744                 minWidth:250,
4745                 scope : scope,
4746                 prompt:true,
4747                 multiline: multiline,
4748                 modal : true
4749             });
4750             return this;
4751         },
4752
4753         /**
4754          * Button config that displays a single OK button
4755          * @type Object
4756          */
4757         OK : {ok:true},
4758         /**
4759          * Button config that displays Yes and No buttons
4760          * @type Object
4761          */
4762         YESNO : {yes:true, no:true},
4763         /**
4764          * Button config that displays OK and Cancel buttons
4765          * @type Object
4766          */
4767         OKCANCEL : {ok:true, cancel:true},
4768         /**
4769          * Button config that displays Yes, No and Cancel buttons
4770          * @type Object
4771          */
4772         YESNOCANCEL : {yes:true, no:true, cancel:true},
4773
4774         /**
4775          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4776          * @type Number
4777          */
4778         defaultTextHeight : 75,
4779         /**
4780          * The maximum width in pixels of the message box (defaults to 600)
4781          * @type Number
4782          */
4783         maxWidth : 600,
4784         /**
4785          * The minimum width in pixels of the message box (defaults to 100)
4786          * @type Number
4787          */
4788         minWidth : 100,
4789         /**
4790          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4791          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4792          * @type Number
4793          */
4794         minProgressWidth : 250,
4795         /**
4796          * An object containing the default button text strings that can be overriden for localized language support.
4797          * Supported properties are: ok, cancel, yes and no.
4798          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4799          * @type Object
4800          */
4801         buttonText : {
4802             ok : "OK",
4803             cancel : "Cancel",
4804             yes : "Yes",
4805             no : "No"
4806         }
4807     };
4808 }();
4809
4810 /**
4811  * Shorthand for {@link Roo.MessageBox}
4812  */
4813 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4814 Roo.Msg = Roo.Msg || Roo.MessageBox;
4815 /*
4816  * - LGPL
4817  *
4818  * navbar
4819  * 
4820  */
4821
4822 /**
4823  * @class Roo.bootstrap.Navbar
4824  * @extends Roo.bootstrap.Component
4825  * Bootstrap Navbar class
4826
4827  * @constructor
4828  * Create a new Navbar
4829  * @param {Object} config The config object
4830  */
4831
4832
4833 Roo.bootstrap.Navbar = function(config){
4834     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4835     this.addEvents({
4836         // raw events
4837         /**
4838          * @event beforetoggle
4839          * Fire before toggle the menu
4840          * @param {Roo.EventObject} e
4841          */
4842         "beforetoggle" : true
4843     });
4844 };
4845
4846 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4847     
4848     
4849    
4850     // private
4851     navItems : false,
4852     loadMask : false,
4853     
4854     
4855     getAutoCreate : function(){
4856         
4857         
4858         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4859         
4860     },
4861     
4862     initEvents :function ()
4863     {
4864         //Roo.log(this.el.select('.navbar-toggle',true));
4865         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4866         
4867         var mark = {
4868             tag: "div",
4869             cls:"x-dlg-mask"
4870         };
4871         
4872         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4873         
4874         var size = this.el.getSize();
4875         this.maskEl.setSize(size.width, size.height);
4876         this.maskEl.enableDisplayMode("block");
4877         this.maskEl.hide();
4878         
4879         if(this.loadMask){
4880             this.maskEl.show();
4881         }
4882     },
4883     
4884     
4885     getChildContainer : function()
4886     {
4887         if (this.el && this.el.select('.collapse').getCount()) {
4888             return this.el.select('.collapse',true).first();
4889         }
4890         
4891         return this.el;
4892     },
4893     
4894     mask : function()
4895     {
4896         this.maskEl.show();
4897     },
4898     
4899     unmask : function()
4900     {
4901         this.maskEl.hide();
4902     },
4903     onToggle : function()
4904     {
4905         
4906         if(this.fireEvent('beforetoggle', this) === false){
4907             return;
4908         }
4909         var ce = this.el.select('.navbar-collapse',true).first();
4910       
4911         if (!ce.hasClass('show')) {
4912            this.expand();
4913         } else {
4914             this.collapse();
4915         }
4916         
4917         
4918     
4919     },
4920     /**
4921      * Expand the navbar pulldown 
4922      */
4923     expand : function ()
4924     {
4925        
4926         var ce = this.el.select('.navbar-collapse',true).first();
4927         if (ce.hasClass('collapsing')) {
4928             return;
4929         }
4930         ce.dom.style.height = '';
4931                // show it...
4932         ce.addClass('in'); // old...
4933         ce.removeClass('collapse');
4934         ce.addClass('show');
4935         var h = ce.getHeight();
4936         Roo.log(h);
4937         ce.removeClass('show');
4938         // at this point we should be able to see it..
4939         ce.addClass('collapsing');
4940         
4941         ce.setHeight(0); // resize it ...
4942         ce.on('transitionend', function() {
4943             //Roo.log('done transition');
4944             ce.removeClass('collapsing');
4945             ce.addClass('show');
4946             ce.removeClass('collapse');
4947
4948             ce.dom.style.height = '';
4949         }, this, { single: true} );
4950         ce.setHeight(h);
4951         ce.dom.scrollTop = 0;
4952     },
4953     /**
4954      * Collapse the navbar pulldown 
4955      */
4956     collapse : function()
4957     {
4958          var ce = this.el.select('.navbar-collapse',true).first();
4959        
4960         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4961             // it's collapsed or collapsing..
4962             return;
4963         }
4964         ce.removeClass('in'); // old...
4965         ce.setHeight(ce.getHeight());
4966         ce.removeClass('show');
4967         ce.addClass('collapsing');
4968         
4969         ce.on('transitionend', function() {
4970             ce.dom.style.height = '';
4971             ce.removeClass('collapsing');
4972             ce.addClass('collapse');
4973         }, this, { single: true} );
4974         ce.setHeight(0);
4975     }
4976     
4977     
4978     
4979 });
4980
4981
4982
4983  
4984
4985  /*
4986  * - LGPL
4987  *
4988  * navbar
4989  * 
4990  */
4991
4992 /**
4993  * @class Roo.bootstrap.NavSimplebar
4994  * @extends Roo.bootstrap.Navbar
4995  * Bootstrap Sidebar class
4996  *
4997  * @cfg {Boolean} inverse is inverted color
4998  * 
4999  * @cfg {String} type (nav | pills | tabs)
5000  * @cfg {Boolean} arrangement stacked | justified
5001  * @cfg {String} align (left | right) alignment
5002  * 
5003  * @cfg {Boolean} main (true|false) main nav bar? default false
5004  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5005  * 
5006  * @cfg {String} tag (header|footer|nav|div) default is nav 
5007
5008  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5009  * 
5010  * 
5011  * @constructor
5012  * Create a new Sidebar
5013  * @param {Object} config The config object
5014  */
5015
5016
5017 Roo.bootstrap.NavSimplebar = function(config){
5018     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5019 };
5020
5021 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5022     
5023     inverse: false,
5024     
5025     type: false,
5026     arrangement: '',
5027     align : false,
5028     
5029     weight : 'light',
5030     
5031     main : false,
5032     
5033     
5034     tag : false,
5035     
5036     
5037     getAutoCreate : function(){
5038         
5039         
5040         var cfg = {
5041             tag : this.tag || 'div',
5042             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5043         };
5044         if (['light','white'].indexOf(this.weight) > -1) {
5045             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5046         }
5047         cfg.cls += ' bg-' + this.weight;
5048         
5049         if (this.inverse) {
5050             cfg.cls += ' navbar-inverse';
5051             
5052         }
5053         
5054         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5055         
5056         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5057             return cfg;
5058         }
5059         
5060         
5061     
5062         
5063         cfg.cn = [
5064             {
5065                 cls: 'nav nav-' + this.xtype,
5066                 tag : 'ul'
5067             }
5068         ];
5069         
5070          
5071         this.type = this.type || 'nav';
5072         if (['tabs','pills'].indexOf(this.type) != -1) {
5073             cfg.cn[0].cls += ' nav-' + this.type
5074         
5075         
5076         } else {
5077             if (this.type!=='nav') {
5078                 Roo.log('nav type must be nav/tabs/pills')
5079             }
5080             cfg.cn[0].cls += ' navbar-nav'
5081         }
5082         
5083         
5084         
5085         
5086         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5087             cfg.cn[0].cls += ' nav-' + this.arrangement;
5088         }
5089         
5090         
5091         if (this.align === 'right') {
5092             cfg.cn[0].cls += ' navbar-right';
5093         }
5094         
5095         
5096         
5097         
5098         return cfg;
5099     
5100         
5101     }
5102     
5103     
5104     
5105 });
5106
5107
5108
5109  
5110
5111  
5112        /*
5113  * - LGPL
5114  *
5115  * navbar
5116  * navbar-fixed-top
5117  * navbar-expand-md  fixed-top 
5118  */
5119
5120 /**
5121  * @class Roo.bootstrap.NavHeaderbar
5122  * @extends Roo.bootstrap.NavSimplebar
5123  * Bootstrap Sidebar class
5124  *
5125  * @cfg {String} brand what is brand
5126  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5127  * @cfg {String} brand_href href of the brand
5128  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5129  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5130  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5131  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5132  * 
5133  * @constructor
5134  * Create a new Sidebar
5135  * @param {Object} config The config object
5136  */
5137
5138
5139 Roo.bootstrap.NavHeaderbar = function(config){
5140     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5141       
5142 };
5143
5144 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5145     
5146     position: '',
5147     brand: '',
5148     brand_href: false,
5149     srButton : true,
5150     autohide : false,
5151     desktopCenter : false,
5152    
5153     
5154     getAutoCreate : function(){
5155         
5156         var   cfg = {
5157             tag: this.nav || 'nav',
5158             cls: 'navbar navbar-expand-md',
5159             role: 'navigation',
5160             cn: []
5161         };
5162         
5163         var cn = cfg.cn;
5164         if (this.desktopCenter) {
5165             cn.push({cls : 'container', cn : []});
5166             cn = cn[0].cn;
5167         }
5168         
5169         if(this.srButton){
5170             var btn = {
5171                 tag: 'button',
5172                 type: 'button',
5173                 cls: 'navbar-toggle navbar-toggler',
5174                 'data-toggle': 'collapse',
5175                 cn: [
5176                     {
5177                         tag: 'span',
5178                         cls: 'sr-only',
5179                         html: 'Toggle navigation'
5180                     },
5181                     {
5182                         tag: 'span',
5183                         cls: 'icon-bar navbar-toggler-icon'
5184                     },
5185                     {
5186                         tag: 'span',
5187                         cls: 'icon-bar'
5188                     },
5189                     {
5190                         tag: 'span',
5191                         cls: 'icon-bar'
5192                     }
5193                 ]
5194             };
5195             
5196             cn.push( Roo.bootstrap.version == 4 ? btn : {
5197                 tag: 'div',
5198                 cls: 'navbar-header',
5199                 cn: [
5200                     btn
5201                 ]
5202             });
5203         }
5204         
5205         cn.push({
5206             tag: 'div',
5207             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5208             cn : []
5209         });
5210         
5211         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5212         
5213         if (['light','white'].indexOf(this.weight) > -1) {
5214             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5215         }
5216         cfg.cls += ' bg-' + this.weight;
5217         
5218         
5219         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5220             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5221             
5222             // tag can override this..
5223             
5224             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5225         }
5226         
5227         if (this.brand !== '') {
5228             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5229             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5230                 tag: 'a',
5231                 href: this.brand_href ? this.brand_href : '#',
5232                 cls: 'navbar-brand',
5233                 cn: [
5234                 this.brand
5235                 ]
5236             });
5237         }
5238         
5239         if(this.main){
5240             cfg.cls += ' main-nav';
5241         }
5242         
5243         
5244         return cfg;
5245
5246         
5247     },
5248     getHeaderChildContainer : function()
5249     {
5250         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5251             return this.el.select('.navbar-header',true).first();
5252         }
5253         
5254         return this.getChildContainer();
5255     },
5256     
5257     getChildContainer : function()
5258     {
5259          
5260         return this.el.select('.roo-navbar-collapse',true).first();
5261          
5262         
5263     },
5264     
5265     initEvents : function()
5266     {
5267         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5268         
5269         if (this.autohide) {
5270             
5271             var prevScroll = 0;
5272             var ft = this.el;
5273             
5274             Roo.get(document).on('scroll',function(e) {
5275                 var ns = Roo.get(document).getScroll().top;
5276                 var os = prevScroll;
5277                 prevScroll = ns;
5278                 
5279                 if(ns > os){
5280                     ft.removeClass('slideDown');
5281                     ft.addClass('slideUp');
5282                     return;
5283                 }
5284                 ft.removeClass('slideUp');
5285                 ft.addClass('slideDown');
5286                  
5287               
5288           },this);
5289         }
5290     }    
5291     
5292 });
5293
5294
5295
5296  
5297
5298  /*
5299  * - LGPL
5300  *
5301  * navbar
5302  * 
5303  */
5304
5305 /**
5306  * @class Roo.bootstrap.NavSidebar
5307  * @extends Roo.bootstrap.Navbar
5308  * Bootstrap Sidebar class
5309  * 
5310  * @constructor
5311  * Create a new Sidebar
5312  * @param {Object} config The config object
5313  */
5314
5315
5316 Roo.bootstrap.NavSidebar = function(config){
5317     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5318 };
5319
5320 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5321     
5322     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5323     
5324     getAutoCreate : function(){
5325         
5326         
5327         return  {
5328             tag: 'div',
5329             cls: 'sidebar sidebar-nav'
5330         };
5331     
5332         
5333     }
5334     
5335     
5336     
5337 });
5338
5339
5340
5341  
5342
5343  /*
5344  * - LGPL
5345  *
5346  * nav group
5347  * 
5348  */
5349
5350 /**
5351  * @class Roo.bootstrap.NavGroup
5352  * @extends Roo.bootstrap.Component
5353  * Bootstrap NavGroup class
5354  * @cfg {String} align (left|right)
5355  * @cfg {Boolean} inverse
5356  * @cfg {String} type (nav|pills|tab) default nav
5357  * @cfg {String} navId - reference Id for navbar.
5358
5359  * 
5360  * @constructor
5361  * Create a new nav group
5362  * @param {Object} config The config object
5363  */
5364
5365 Roo.bootstrap.NavGroup = function(config){
5366     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5367     this.navItems = [];
5368    
5369     Roo.bootstrap.NavGroup.register(this);
5370      this.addEvents({
5371         /**
5372              * @event changed
5373              * Fires when the active item changes
5374              * @param {Roo.bootstrap.NavGroup} this
5375              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5376              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5377          */
5378         'changed': true
5379      });
5380     
5381 };
5382
5383 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5384     
5385     align: '',
5386     inverse: false,
5387     form: false,
5388     type: 'nav',
5389     navId : '',
5390     // private
5391     
5392     navItems : false, 
5393     
5394     getAutoCreate : function()
5395     {
5396         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5397         
5398         cfg = {
5399             tag : 'ul',
5400             cls: 'nav' 
5401         };
5402         if (Roo.bootstrap.version == 4) {
5403             if (['tabs','pills'].indexOf(this.type) != -1) {
5404                 cfg.cls += ' nav-' + this.type; 
5405             } else {
5406                 // trying to remove so header bar can right align top?
5407                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5408                     // do not use on header bar... 
5409                     cfg.cls += ' navbar-nav';
5410                 }
5411             }
5412             
5413         } else {
5414             if (['tabs','pills'].indexOf(this.type) != -1) {
5415                 cfg.cls += ' nav-' + this.type
5416             } else {
5417                 if (this.type !== 'nav') {
5418                     Roo.log('nav type must be nav/tabs/pills')
5419                 }
5420                 cfg.cls += ' navbar-nav'
5421             }
5422         }
5423         
5424         if (this.parent() && this.parent().sidebar) {
5425             cfg = {
5426                 tag: 'ul',
5427                 cls: 'dashboard-menu sidebar-menu'
5428             };
5429             
5430             return cfg;
5431         }
5432         
5433         if (this.form === true) {
5434             cfg = {
5435                 tag: 'form',
5436                 cls: 'navbar-form form-inline'
5437             };
5438             //nav navbar-right ml-md-auto
5439             if (this.align === 'right') {
5440                 cfg.cls += ' navbar-right ml-md-auto';
5441             } else {
5442                 cfg.cls += ' navbar-left';
5443             }
5444         }
5445         
5446         if (this.align === 'right') {
5447             cfg.cls += ' navbar-right ml-md-auto';
5448         } else {
5449             cfg.cls += ' mr-auto';
5450         }
5451         
5452         if (this.inverse) {
5453             cfg.cls += ' navbar-inverse';
5454             
5455         }
5456         
5457         
5458         return cfg;
5459     },
5460     /**
5461     * sets the active Navigation item
5462     * @param {Roo.bootstrap.NavItem} the new current navitem
5463     */
5464     setActiveItem : function(item)
5465     {
5466         var prev = false;
5467         Roo.each(this.navItems, function(v){
5468             if (v == item) {
5469                 return ;
5470             }
5471             if (v.isActive()) {
5472                 v.setActive(false, true);
5473                 prev = v;
5474                 
5475             }
5476             
5477         });
5478
5479         item.setActive(true, true);
5480         this.fireEvent('changed', this, item, prev);
5481         
5482         
5483     },
5484     /**
5485     * gets the active Navigation item
5486     * @return {Roo.bootstrap.NavItem} the current navitem
5487     */
5488     getActive : function()
5489     {
5490         
5491         var prev = false;
5492         Roo.each(this.navItems, function(v){
5493             
5494             if (v.isActive()) {
5495                 prev = v;
5496                 
5497             }
5498             
5499         });
5500         return prev;
5501     },
5502     
5503     indexOfNav : function()
5504     {
5505         
5506         var prev = false;
5507         Roo.each(this.navItems, function(v,i){
5508             
5509             if (v.isActive()) {
5510                 prev = i;
5511                 
5512             }
5513             
5514         });
5515         return prev;
5516     },
5517     /**
5518     * adds a Navigation item
5519     * @param {Roo.bootstrap.NavItem} the navitem to add
5520     */
5521     addItem : function(cfg)
5522     {
5523         if (this.form && Roo.bootstrap.version == 4) {
5524             cfg.tag = 'div';
5525         }
5526         var cn = new Roo.bootstrap.NavItem(cfg);
5527         this.register(cn);
5528         cn.parentId = this.id;
5529         cn.onRender(this.el, null);
5530         return cn;
5531     },
5532     /**
5533     * register a Navigation item
5534     * @param {Roo.bootstrap.NavItem} the navitem to add
5535     */
5536     register : function(item)
5537     {
5538         this.navItems.push( item);
5539         item.navId = this.navId;
5540     
5541     },
5542     
5543     /**
5544     * clear all the Navigation item
5545     */
5546    
5547     clearAll : function()
5548     {
5549         this.navItems = [];
5550         this.el.dom.innerHTML = '';
5551     },
5552     
5553     getNavItem: function(tabId)
5554     {
5555         var ret = false;
5556         Roo.each(this.navItems, function(e) {
5557             if (e.tabId == tabId) {
5558                ret =  e;
5559                return false;
5560             }
5561             return true;
5562             
5563         });
5564         return ret;
5565     },
5566     
5567     setActiveNext : function()
5568     {
5569         var i = this.indexOfNav(this.getActive());
5570         if (i > this.navItems.length) {
5571             return;
5572         }
5573         this.setActiveItem(this.navItems[i+1]);
5574     },
5575     setActivePrev : function()
5576     {
5577         var i = this.indexOfNav(this.getActive());
5578         if (i  < 1) {
5579             return;
5580         }
5581         this.setActiveItem(this.navItems[i-1]);
5582     },
5583     clearWasActive : function(except) {
5584         Roo.each(this.navItems, function(e) {
5585             if (e.tabId != except.tabId && e.was_active) {
5586                e.was_active = false;
5587                return false;
5588             }
5589             return true;
5590             
5591         });
5592     },
5593     getWasActive : function ()
5594     {
5595         var r = false;
5596         Roo.each(this.navItems, function(e) {
5597             if (e.was_active) {
5598                r = e;
5599                return false;
5600             }
5601             return true;
5602             
5603         });
5604         return r;
5605     }
5606     
5607     
5608 });
5609
5610  
5611 Roo.apply(Roo.bootstrap.NavGroup, {
5612     
5613     groups: {},
5614      /**
5615     * register a Navigation Group
5616     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5617     */
5618     register : function(navgrp)
5619     {
5620         this.groups[navgrp.navId] = navgrp;
5621         
5622     },
5623     /**
5624     * fetch a Navigation Group based on the navigation ID
5625     * @param {string} the navgroup to add
5626     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5627     */
5628     get: function(navId) {
5629         if (typeof(this.groups[navId]) == 'undefined') {
5630             return false;
5631             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5632         }
5633         return this.groups[navId] ;
5634     }
5635     
5636     
5637     
5638 });
5639
5640  /*
5641  * - LGPL
5642  *
5643  * row
5644  * 
5645  */
5646
5647 /**
5648  * @class Roo.bootstrap.NavItem
5649  * @extends Roo.bootstrap.Component
5650  * Bootstrap Navbar.NavItem class
5651  * @cfg {String} href  link to
5652  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5653
5654  * @cfg {String} html content of button
5655  * @cfg {String} badge text inside badge
5656  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5657  * @cfg {String} glyphicon DEPRICATED - use fa
5658  * @cfg {String} icon DEPRICATED - use fa
5659  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5660  * @cfg {Boolean} active Is item active
5661  * @cfg {Boolean} disabled Is item disabled
5662  
5663  * @cfg {Boolean} preventDefault (true | false) default false
5664  * @cfg {String} tabId the tab that this item activates.
5665  * @cfg {String} tagtype (a|span) render as a href or span?
5666  * @cfg {Boolean} animateRef (true|false) link to element default false  
5667   
5668  * @constructor
5669  * Create a new Navbar Item
5670  * @param {Object} config The config object
5671  */
5672 Roo.bootstrap.NavItem = function(config){
5673     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5674     this.addEvents({
5675         // raw events
5676         /**
5677          * @event click
5678          * The raw click event for the entire grid.
5679          * @param {Roo.EventObject} e
5680          */
5681         "click" : true,
5682          /**
5683             * @event changed
5684             * Fires when the active item active state changes
5685             * @param {Roo.bootstrap.NavItem} this
5686             * @param {boolean} state the new state
5687              
5688          */
5689         'changed': true,
5690         /**
5691             * @event scrollto
5692             * Fires when scroll to element
5693             * @param {Roo.bootstrap.NavItem} this
5694             * @param {Object} options
5695             * @param {Roo.EventObject} e
5696              
5697          */
5698         'scrollto': true
5699     });
5700    
5701 };
5702
5703 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5704     
5705     href: false,
5706     html: '',
5707     badge: '',
5708     icon: false,
5709     fa : false,
5710     glyphicon: false,
5711     active: false,
5712     preventDefault : false,
5713     tabId : false,
5714     tagtype : 'a',
5715     tag: 'li',
5716     disabled : false,
5717     animateRef : false,
5718     was_active : false,
5719     button_weight : '',
5720     button_outline : false,
5721     
5722     navLink: false,
5723     
5724     getAutoCreate : function(){
5725          
5726         var cfg = {
5727             tag: this.tag,
5728             cls: 'nav-item'
5729         };
5730         
5731         if (this.active) {
5732             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5733         }
5734         if (this.disabled) {
5735             cfg.cls += ' disabled';
5736         }
5737         
5738         // BS4 only?
5739         if (this.button_weight.length) {
5740             cfg.tag = this.href ? 'a' : 'button';
5741             cfg.html = this.html || '';
5742             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5743             if (this.href) {
5744                 cfg.href = this.href;
5745             }
5746             if (this.fa) {
5747                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5748             }
5749             
5750             // menu .. should add dropdown-menu class - so no need for carat..
5751             
5752             if (this.badge !== '') {
5753                  
5754                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5755             }
5756             return cfg;
5757         }
5758         
5759         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5760             cfg.cn = [
5761                 {
5762                     tag: this.tagtype,
5763                     href : this.href || "#",
5764                     html: this.html || ''
5765                 }
5766             ];
5767             if (this.tagtype == 'a') {
5768                 cfg.cn[0].cls = 'nav-link';
5769             }
5770             if (this.icon) {
5771                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5772             }
5773             if (this.fa) {
5774                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5775             }
5776             if(this.glyphicon) {
5777                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5778             }
5779             
5780             if (this.menu) {
5781                 
5782                 cfg.cn[0].html += " <span class='caret'></span>";
5783              
5784             }
5785             
5786             if (this.badge !== '') {
5787                  
5788                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5789             }
5790         }
5791         
5792         
5793         
5794         return cfg;
5795     },
5796     onRender : function(ct, position)
5797     {
5798        // Roo.log("Call onRender: " + this.xtype);
5799         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5800             this.tag = 'div';
5801         }
5802         
5803         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5804         this.navLink = this.el.select('.nav-link',true).first();
5805         return ret;
5806     },
5807       
5808     
5809     initEvents: function() 
5810     {
5811         if (typeof (this.menu) != 'undefined') {
5812             this.menu.parentType = this.xtype;
5813             this.menu.triggerEl = this.el;
5814             this.menu = this.addxtype(Roo.apply({}, this.menu));
5815         }
5816         
5817         this.el.select('a',true).on('click', this.onClick, this);
5818         
5819         if(this.tagtype == 'span'){
5820             this.el.select('span',true).on('click', this.onClick, this);
5821         }
5822        
5823         // at this point parent should be available..
5824         this.parent().register(this);
5825     },
5826     
5827     onClick : function(e)
5828     {
5829         if (e.getTarget('.dropdown-menu-item')) {
5830             // did you click on a menu itemm.... - then don't trigger onclick..
5831             return;
5832         }
5833         
5834         if(
5835                 this.preventDefault || 
5836                 this.href == '#' 
5837         ){
5838             Roo.log("NavItem - prevent Default?");
5839             e.preventDefault();
5840         }
5841         
5842         if (this.disabled) {
5843             return;
5844         }
5845         
5846         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5847         if (tg && tg.transition) {
5848             Roo.log("waiting for the transitionend");
5849             return;
5850         }
5851         
5852         
5853         
5854         //Roo.log("fire event clicked");
5855         if(this.fireEvent('click', this, e) === false){
5856             return;
5857         };
5858         
5859         if(this.tagtype == 'span'){
5860             return;
5861         }
5862         
5863         //Roo.log(this.href);
5864         var ael = this.el.select('a',true).first();
5865         //Roo.log(ael);
5866         
5867         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5868             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5869             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5870                 return; // ignore... - it's a 'hash' to another page.
5871             }
5872             Roo.log("NavItem - prevent Default?");
5873             e.preventDefault();
5874             this.scrollToElement(e);
5875         }
5876         
5877         
5878         var p =  this.parent();
5879    
5880         if (['tabs','pills'].indexOf(p.type)!==-1) {
5881             if (typeof(p.setActiveItem) !== 'undefined') {
5882                 p.setActiveItem(this);
5883             }
5884         }
5885         
5886         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5887         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5888             // remove the collapsed menu expand...
5889             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5890         }
5891     },
5892     
5893     isActive: function () {
5894         return this.active
5895     },
5896     setActive : function(state, fire, is_was_active)
5897     {
5898         if (this.active && !state && this.navId) {
5899             this.was_active = true;
5900             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5901             if (nv) {
5902                 nv.clearWasActive(this);
5903             }
5904             
5905         }
5906         this.active = state;
5907         
5908         if (!state ) {
5909             this.el.removeClass('active');
5910             this.navLink ? this.navLink.removeClass('active') : false;
5911         } else if (!this.el.hasClass('active')) {
5912             
5913             this.el.addClass('active');
5914             if (Roo.bootstrap.version == 4 && this.navLink ) {
5915                 this.navLink.addClass('active');
5916             }
5917             
5918         }
5919         if (fire) {
5920             this.fireEvent('changed', this, state);
5921         }
5922         
5923         // show a panel if it's registered and related..
5924         
5925         if (!this.navId || !this.tabId || !state || is_was_active) {
5926             return;
5927         }
5928         
5929         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5930         if (!tg) {
5931             return;
5932         }
5933         var pan = tg.getPanelByName(this.tabId);
5934         if (!pan) {
5935             return;
5936         }
5937         // if we can not flip to new panel - go back to old nav highlight..
5938         if (false == tg.showPanel(pan)) {
5939             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5940             if (nv) {
5941                 var onav = nv.getWasActive();
5942                 if (onav) {
5943                     onav.setActive(true, false, true);
5944                 }
5945             }
5946             
5947         }
5948         
5949         
5950         
5951     },
5952      // this should not be here...
5953     setDisabled : function(state)
5954     {
5955         this.disabled = state;
5956         if (!state ) {
5957             this.el.removeClass('disabled');
5958         } else if (!this.el.hasClass('disabled')) {
5959             this.el.addClass('disabled');
5960         }
5961         
5962     },
5963     
5964     /**
5965      * Fetch the element to display the tooltip on.
5966      * @return {Roo.Element} defaults to this.el
5967      */
5968     tooltipEl : function()
5969     {
5970         return this.el.select('' + this.tagtype + '', true).first();
5971     },
5972     
5973     scrollToElement : function(e)
5974     {
5975         var c = document.body;
5976         
5977         /*
5978          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5979          */
5980         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5981             c = document.documentElement;
5982         }
5983         
5984         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5985         
5986         if(!target){
5987             return;
5988         }
5989
5990         var o = target.calcOffsetsTo(c);
5991         
5992         var options = {
5993             target : target,
5994             value : o[1]
5995         };
5996         
5997         this.fireEvent('scrollto', this, options, e);
5998         
5999         Roo.get(c).scrollTo('top', options.value, true);
6000         
6001         return;
6002     }
6003 });
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * sidebar item
6010  *
6011  *  li
6012  *    <span> icon </span>
6013  *    <span> text </span>
6014  *    <span>badge </span>
6015  */
6016
6017 /**
6018  * @class Roo.bootstrap.NavSidebarItem
6019  * @extends Roo.bootstrap.NavItem
6020  * Bootstrap Navbar.NavSidebarItem class
6021  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6022  * {Boolean} open is the menu open
6023  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6024  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6025  * {String} buttonSize (sm|md|lg)the extra classes for the button
6026  * {Boolean} showArrow show arrow next to the text (default true)
6027  * @constructor
6028  * Create a new Navbar Button
6029  * @param {Object} config The config object
6030  */
6031 Roo.bootstrap.NavSidebarItem = function(config){
6032     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6033     this.addEvents({
6034         // raw events
6035         /**
6036          * @event click
6037          * The raw click event for the entire grid.
6038          * @param {Roo.EventObject} e
6039          */
6040         "click" : true,
6041          /**
6042             * @event changed
6043             * Fires when the active item active state changes
6044             * @param {Roo.bootstrap.NavSidebarItem} this
6045             * @param {boolean} state the new state
6046              
6047          */
6048         'changed': true
6049     });
6050    
6051 };
6052
6053 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6054     
6055     badgeWeight : 'default',
6056     
6057     open: false,
6058     
6059     buttonView : false,
6060     
6061     buttonWeight : 'default',
6062     
6063     buttonSize : 'md',
6064     
6065     showArrow : true,
6066     
6067     getAutoCreate : function(){
6068         
6069         
6070         var a = {
6071                 tag: 'a',
6072                 href : this.href || '#',
6073                 cls: '',
6074                 html : '',
6075                 cn : []
6076         };
6077         
6078         if(this.buttonView){
6079             a = {
6080                 tag: 'button',
6081                 href : this.href || '#',
6082                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6083                 html : this.html,
6084                 cn : []
6085             };
6086         }
6087         
6088         var cfg = {
6089             tag: 'li',
6090             cls: '',
6091             cn: [ a ]
6092         };
6093         
6094         if (this.active) {
6095             cfg.cls += ' active';
6096         }
6097         
6098         if (this.disabled) {
6099             cfg.cls += ' disabled';
6100         }
6101         if (this.open) {
6102             cfg.cls += ' open x-open';
6103         }
6104         // left icon..
6105         if (this.glyphicon || this.icon) {
6106             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6107             a.cn.push({ tag : 'i', cls : c }) ;
6108         }
6109         
6110         if(!this.buttonView){
6111             var span = {
6112                 tag: 'span',
6113                 html : this.html || ''
6114             };
6115
6116             a.cn.push(span);
6117             
6118         }
6119         
6120         if (this.badge !== '') {
6121             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6122         }
6123         
6124         if (this.menu) {
6125             
6126             if(this.showArrow){
6127                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6128             }
6129             
6130             a.cls += ' dropdown-toggle treeview' ;
6131         }
6132         
6133         return cfg;
6134     },
6135     
6136     initEvents : function()
6137     { 
6138         if (typeof (this.menu) != 'undefined') {
6139             this.menu.parentType = this.xtype;
6140             this.menu.triggerEl = this.el;
6141             this.menu = this.addxtype(Roo.apply({}, this.menu));
6142         }
6143         
6144         this.el.on('click', this.onClick, this);
6145         
6146         if(this.badge !== ''){
6147             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6148         }
6149         
6150     },
6151     
6152     onClick : function(e)
6153     {
6154         if(this.disabled){
6155             e.preventDefault();
6156             return;
6157         }
6158         
6159         if(this.preventDefault){
6160             e.preventDefault();
6161         }
6162         
6163         this.fireEvent('click', this, e);
6164     },
6165     
6166     disable : function()
6167     {
6168         this.setDisabled(true);
6169     },
6170     
6171     enable : function()
6172     {
6173         this.setDisabled(false);
6174     },
6175     
6176     setDisabled : function(state)
6177     {
6178         if(this.disabled == state){
6179             return;
6180         }
6181         
6182         this.disabled = state;
6183         
6184         if (state) {
6185             this.el.addClass('disabled');
6186             return;
6187         }
6188         
6189         this.el.removeClass('disabled');
6190         
6191         return;
6192     },
6193     
6194     setActive : function(state)
6195     {
6196         if(this.active == state){
6197             return;
6198         }
6199         
6200         this.active = state;
6201         
6202         if (state) {
6203             this.el.addClass('active');
6204             return;
6205         }
6206         
6207         this.el.removeClass('active');
6208         
6209         return;
6210     },
6211     
6212     isActive: function () 
6213     {
6214         return this.active;
6215     },
6216     
6217     setBadge : function(str)
6218     {
6219         if(!this.badgeEl){
6220             return;
6221         }
6222         
6223         this.badgeEl.dom.innerHTML = str;
6224     }
6225     
6226    
6227      
6228  
6229 });
6230  
6231
6232  /*
6233  * - LGPL
6234  *
6235  * row
6236  * 
6237  */
6238
6239 /**
6240  * @class Roo.bootstrap.Row
6241  * @extends Roo.bootstrap.Component
6242  * Bootstrap Row class (contains columns...)
6243  * 
6244  * @constructor
6245  * Create a new Row
6246  * @param {Object} config The config object
6247  */
6248
6249 Roo.bootstrap.Row = function(config){
6250     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6251 };
6252
6253 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6254     
6255     getAutoCreate : function(){
6256        return {
6257             cls: 'row clearfix'
6258        };
6259     }
6260     
6261     
6262 });
6263
6264  
6265
6266  /*
6267  * - LGPL
6268  *
6269  * pagination
6270  * 
6271  */
6272
6273 /**
6274  * @class Roo.bootstrap.Pagination
6275  * @extends Roo.bootstrap.Component
6276  * Bootstrap Pagination class
6277  * @cfg {String} size xs | sm | md | lg
6278  * @cfg {Boolean} inverse false | true
6279  * 
6280  * @constructor
6281  * Create a new Pagination
6282  * @param {Object} config The config object
6283  */
6284
6285 Roo.bootstrap.Pagination = function(config){
6286     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6287 };
6288
6289 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6290     
6291     cls: false,
6292     size: false,
6293     inverse: false,
6294     
6295     getAutoCreate : function(){
6296         var cfg = {
6297             tag: 'ul',
6298                 cls: 'pagination'
6299         };
6300         if (this.inverse) {
6301             cfg.cls += ' inverse';
6302         }
6303         if (this.html) {
6304             cfg.html=this.html;
6305         }
6306         if (this.cls) {
6307             cfg.cls += " " + this.cls;
6308         }
6309         return cfg;
6310     }
6311    
6312 });
6313
6314  
6315
6316  /*
6317  * - LGPL
6318  *
6319  * Pagination item
6320  * 
6321  */
6322
6323
6324 /**
6325  * @class Roo.bootstrap.PaginationItem
6326  * @extends Roo.bootstrap.Component
6327  * Bootstrap PaginationItem class
6328  * @cfg {String} html text
6329  * @cfg {String} href the link
6330  * @cfg {Boolean} preventDefault (true | false) default true
6331  * @cfg {Boolean} active (true | false) default false
6332  * @cfg {Boolean} disabled default false
6333  * 
6334  * 
6335  * @constructor
6336  * Create a new PaginationItem
6337  * @param {Object} config The config object
6338  */
6339
6340
6341 Roo.bootstrap.PaginationItem = function(config){
6342     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6343     this.addEvents({
6344         // raw events
6345         /**
6346          * @event click
6347          * The raw click event for the entire grid.
6348          * @param {Roo.EventObject} e
6349          */
6350         "click" : true
6351     });
6352 };
6353
6354 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6355     
6356     href : false,
6357     html : false,
6358     preventDefault: true,
6359     active : false,
6360     cls : false,
6361     disabled: false,
6362     
6363     getAutoCreate : function(){
6364         var cfg= {
6365             tag: 'li',
6366             cn: [
6367                 {
6368                     tag : 'a',
6369                     href : this.href ? this.href : '#',
6370                     html : this.html ? this.html : ''
6371                 }
6372             ]
6373         };
6374         
6375         if(this.cls){
6376             cfg.cls = this.cls;
6377         }
6378         
6379         if(this.disabled){
6380             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6381         }
6382         
6383         if(this.active){
6384             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6385         }
6386         
6387         return cfg;
6388     },
6389     
6390     initEvents: function() {
6391         
6392         this.el.on('click', this.onClick, this);
6393         
6394     },
6395     onClick : function(e)
6396     {
6397         Roo.log('PaginationItem on click ');
6398         if(this.preventDefault){
6399             e.preventDefault();
6400         }
6401         
6402         if(this.disabled){
6403             return;
6404         }
6405         
6406         this.fireEvent('click', this, e);
6407     }
6408    
6409 });
6410
6411  
6412
6413  /*
6414  * - LGPL
6415  *
6416  * slider
6417  * 
6418  */
6419
6420
6421 /**
6422  * @class Roo.bootstrap.Slider
6423  * @extends Roo.bootstrap.Component
6424  * Bootstrap Slider class
6425  *    
6426  * @constructor
6427  * Create a new Slider
6428  * @param {Object} config The config object
6429  */
6430
6431 Roo.bootstrap.Slider = function(config){
6432     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6433 };
6434
6435 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6436     
6437     getAutoCreate : function(){
6438         
6439         var cfg = {
6440             tag: 'div',
6441             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6442             cn: [
6443                 {
6444                     tag: 'a',
6445                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6446                 }
6447             ]
6448         };
6449         
6450         return cfg;
6451     }
6452    
6453 });
6454
6455  /*
6456  * Based on:
6457  * Ext JS Library 1.1.1
6458  * Copyright(c) 2006-2007, Ext JS, LLC.
6459  *
6460  * Originally Released Under LGPL - original licence link has changed is not relivant.
6461  *
6462  * Fork - LGPL
6463  * <script type="text/javascript">
6464  */
6465  
6466
6467 /**
6468  * @class Roo.grid.ColumnModel
6469  * @extends Roo.util.Observable
6470  * This is the default implementation of a ColumnModel used by the Grid. It defines
6471  * the columns in the grid.
6472  * <br>Usage:<br>
6473  <pre><code>
6474  var colModel = new Roo.grid.ColumnModel([
6475         {header: "Ticker", width: 60, sortable: true, locked: true},
6476         {header: "Company Name", width: 150, sortable: true},
6477         {header: "Market Cap.", width: 100, sortable: true},
6478         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6479         {header: "Employees", width: 100, sortable: true, resizable: false}
6480  ]);
6481  </code></pre>
6482  * <p>
6483  
6484  * The config options listed for this class are options which may appear in each
6485  * individual column definition.
6486  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6487  * @constructor
6488  * @param {Object} config An Array of column config objects. See this class's
6489  * config objects for details.
6490 */
6491 Roo.grid.ColumnModel = function(config){
6492         /**
6493      * The config passed into the constructor
6494      */
6495     this.config = config;
6496     this.lookup = {};
6497
6498     // if no id, create one
6499     // if the column does not have a dataIndex mapping,
6500     // map it to the order it is in the config
6501     for(var i = 0, len = config.length; i < len; i++){
6502         var c = config[i];
6503         if(typeof c.dataIndex == "undefined"){
6504             c.dataIndex = i;
6505         }
6506         if(typeof c.renderer == "string"){
6507             c.renderer = Roo.util.Format[c.renderer];
6508         }
6509         if(typeof c.id == "undefined"){
6510             c.id = Roo.id();
6511         }
6512         if(c.editor && c.editor.xtype){
6513             c.editor  = Roo.factory(c.editor, Roo.grid);
6514         }
6515         if(c.editor && c.editor.isFormField){
6516             c.editor = new Roo.grid.GridEditor(c.editor);
6517         }
6518         this.lookup[c.id] = c;
6519     }
6520
6521     /**
6522      * The width of columns which have no width specified (defaults to 100)
6523      * @type Number
6524      */
6525     this.defaultWidth = 100;
6526
6527     /**
6528      * Default sortable of columns which have no sortable specified (defaults to false)
6529      * @type Boolean
6530      */
6531     this.defaultSortable = false;
6532
6533     this.addEvents({
6534         /**
6535              * @event widthchange
6536              * Fires when the width of a column changes.
6537              * @param {ColumnModel} this
6538              * @param {Number} columnIndex The column index
6539              * @param {Number} newWidth The new width
6540              */
6541             "widthchange": true,
6542         /**
6543              * @event headerchange
6544              * Fires when the text of a header changes.
6545              * @param {ColumnModel} this
6546              * @param {Number} columnIndex The column index
6547              * @param {Number} newText The new header text
6548              */
6549             "headerchange": true,
6550         /**
6551              * @event hiddenchange
6552              * Fires when a column is hidden or "unhidden".
6553              * @param {ColumnModel} this
6554              * @param {Number} columnIndex The column index
6555              * @param {Boolean} hidden true if hidden, false otherwise
6556              */
6557             "hiddenchange": true,
6558             /**
6559          * @event columnmoved
6560          * Fires when a column is moved.
6561          * @param {ColumnModel} this
6562          * @param {Number} oldIndex
6563          * @param {Number} newIndex
6564          */
6565         "columnmoved" : true,
6566         /**
6567          * @event columlockchange
6568          * Fires when a column's locked state is changed
6569          * @param {ColumnModel} this
6570          * @param {Number} colIndex
6571          * @param {Boolean} locked true if locked
6572          */
6573         "columnlockchange" : true
6574     });
6575     Roo.grid.ColumnModel.superclass.constructor.call(this);
6576 };
6577 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6578     /**
6579      * @cfg {String} header The header text to display in the Grid view.
6580      */
6581     /**
6582      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6583      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6584      * specified, the column's index is used as an index into the Record's data Array.
6585      */
6586     /**
6587      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6588      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6589      */
6590     /**
6591      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6592      * Defaults to the value of the {@link #defaultSortable} property.
6593      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6594      */
6595     /**
6596      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6597      */
6598     /**
6599      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6600      */
6601     /**
6602      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6603      */
6604     /**
6605      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6606      */
6607     /**
6608      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6609      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6610      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6611      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6612      */
6613        /**
6614      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6615      */
6616     /**
6617      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6618      */
6619     /**
6620      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6621      */
6622     /**
6623      * @cfg {String} cursor (Optional)
6624      */
6625     /**
6626      * @cfg {String} tooltip (Optional)
6627      */
6628     /**
6629      * @cfg {Number} xs (Optional)
6630      */
6631     /**
6632      * @cfg {Number} sm (Optional)
6633      */
6634     /**
6635      * @cfg {Number} md (Optional)
6636      */
6637     /**
6638      * @cfg {Number} lg (Optional)
6639      */
6640     /**
6641      * Returns the id of the column at the specified index.
6642      * @param {Number} index The column index
6643      * @return {String} the id
6644      */
6645     getColumnId : function(index){
6646         return this.config[index].id;
6647     },
6648
6649     /**
6650      * Returns the column for a specified id.
6651      * @param {String} id The column id
6652      * @return {Object} the column
6653      */
6654     getColumnById : function(id){
6655         return this.lookup[id];
6656     },
6657
6658     
6659     /**
6660      * Returns the column for a specified dataIndex.
6661      * @param {String} dataIndex The column dataIndex
6662      * @return {Object|Boolean} the column or false if not found
6663      */
6664     getColumnByDataIndex: function(dataIndex){
6665         var index = this.findColumnIndex(dataIndex);
6666         return index > -1 ? this.config[index] : false;
6667     },
6668     
6669     /**
6670      * Returns the index for a specified column id.
6671      * @param {String} id The column id
6672      * @return {Number} the index, or -1 if not found
6673      */
6674     getIndexById : function(id){
6675         for(var i = 0, len = this.config.length; i < len; i++){
6676             if(this.config[i].id == id){
6677                 return i;
6678             }
6679         }
6680         return -1;
6681     },
6682     
6683     /**
6684      * Returns the index for a specified column dataIndex.
6685      * @param {String} dataIndex The column dataIndex
6686      * @return {Number} the index, or -1 if not found
6687      */
6688     
6689     findColumnIndex : function(dataIndex){
6690         for(var i = 0, len = this.config.length; i < len; i++){
6691             if(this.config[i].dataIndex == dataIndex){
6692                 return i;
6693             }
6694         }
6695         return -1;
6696     },
6697     
6698     
6699     moveColumn : function(oldIndex, newIndex){
6700         var c = this.config[oldIndex];
6701         this.config.splice(oldIndex, 1);
6702         this.config.splice(newIndex, 0, c);
6703         this.dataMap = null;
6704         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6705     },
6706
6707     isLocked : function(colIndex){
6708         return this.config[colIndex].locked === true;
6709     },
6710
6711     setLocked : function(colIndex, value, suppressEvent){
6712         if(this.isLocked(colIndex) == value){
6713             return;
6714         }
6715         this.config[colIndex].locked = value;
6716         if(!suppressEvent){
6717             this.fireEvent("columnlockchange", this, colIndex, value);
6718         }
6719     },
6720
6721     getTotalLockedWidth : function(){
6722         var totalWidth = 0;
6723         for(var i = 0; i < this.config.length; i++){
6724             if(this.isLocked(i) && !this.isHidden(i)){
6725                 this.totalWidth += this.getColumnWidth(i);
6726             }
6727         }
6728         return totalWidth;
6729     },
6730
6731     getLockedCount : function(){
6732         for(var i = 0, len = this.config.length; i < len; i++){
6733             if(!this.isLocked(i)){
6734                 return i;
6735             }
6736         }
6737         
6738         return this.config.length;
6739     },
6740
6741     /**
6742      * Returns the number of columns.
6743      * @return {Number}
6744      */
6745     getColumnCount : function(visibleOnly){
6746         if(visibleOnly === true){
6747             var c = 0;
6748             for(var i = 0, len = this.config.length; i < len; i++){
6749                 if(!this.isHidden(i)){
6750                     c++;
6751                 }
6752             }
6753             return c;
6754         }
6755         return this.config.length;
6756     },
6757
6758     /**
6759      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6760      * @param {Function} fn
6761      * @param {Object} scope (optional)
6762      * @return {Array} result
6763      */
6764     getColumnsBy : function(fn, scope){
6765         var r = [];
6766         for(var i = 0, len = this.config.length; i < len; i++){
6767             var c = this.config[i];
6768             if(fn.call(scope||this, c, i) === true){
6769                 r[r.length] = c;
6770             }
6771         }
6772         return r;
6773     },
6774
6775     /**
6776      * Returns true if the specified column is sortable.
6777      * @param {Number} col The column index
6778      * @return {Boolean}
6779      */
6780     isSortable : function(col){
6781         if(typeof this.config[col].sortable == "undefined"){
6782             return this.defaultSortable;
6783         }
6784         return this.config[col].sortable;
6785     },
6786
6787     /**
6788      * Returns the rendering (formatting) function defined for the column.
6789      * @param {Number} col The column index.
6790      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6791      */
6792     getRenderer : function(col){
6793         if(!this.config[col].renderer){
6794             return Roo.grid.ColumnModel.defaultRenderer;
6795         }
6796         return this.config[col].renderer;
6797     },
6798
6799     /**
6800      * Sets the rendering (formatting) function for a column.
6801      * @param {Number} col The column index
6802      * @param {Function} fn The function to use to process the cell's raw data
6803      * to return HTML markup for the grid view. The render function is called with
6804      * the following parameters:<ul>
6805      * <li>Data value.</li>
6806      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6807      * <li>css A CSS style string to apply to the table cell.</li>
6808      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6809      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6810      * <li>Row index</li>
6811      * <li>Column index</li>
6812      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6813      */
6814     setRenderer : function(col, fn){
6815         this.config[col].renderer = fn;
6816     },
6817
6818     /**
6819      * Returns the width for the specified column.
6820      * @param {Number} col The column index
6821      * @return {Number}
6822      */
6823     getColumnWidth : function(col){
6824         return this.config[col].width * 1 || this.defaultWidth;
6825     },
6826
6827     /**
6828      * Sets the width for a column.
6829      * @param {Number} col The column index
6830      * @param {Number} width The new width
6831      */
6832     setColumnWidth : function(col, width, suppressEvent){
6833         this.config[col].width = width;
6834         this.totalWidth = null;
6835         if(!suppressEvent){
6836              this.fireEvent("widthchange", this, col, width);
6837         }
6838     },
6839
6840     /**
6841      * Returns the total width of all columns.
6842      * @param {Boolean} includeHidden True to include hidden column widths
6843      * @return {Number}
6844      */
6845     getTotalWidth : function(includeHidden){
6846         if(!this.totalWidth){
6847             this.totalWidth = 0;
6848             for(var i = 0, len = this.config.length; i < len; i++){
6849                 if(includeHidden || !this.isHidden(i)){
6850                     this.totalWidth += this.getColumnWidth(i);
6851                 }
6852             }
6853         }
6854         return this.totalWidth;
6855     },
6856
6857     /**
6858      * Returns the header for the specified column.
6859      * @param {Number} col The column index
6860      * @return {String}
6861      */
6862     getColumnHeader : function(col){
6863         return this.config[col].header;
6864     },
6865
6866     /**
6867      * Sets the header for a column.
6868      * @param {Number} col The column index
6869      * @param {String} header The new header
6870      */
6871     setColumnHeader : function(col, header){
6872         this.config[col].header = header;
6873         this.fireEvent("headerchange", this, col, header);
6874     },
6875
6876     /**
6877      * Returns the tooltip for the specified column.
6878      * @param {Number} col The column index
6879      * @return {String}
6880      */
6881     getColumnTooltip : function(col){
6882             return this.config[col].tooltip;
6883     },
6884     /**
6885      * Sets the tooltip for a column.
6886      * @param {Number} col The column index
6887      * @param {String} tooltip The new tooltip
6888      */
6889     setColumnTooltip : function(col, tooltip){
6890             this.config[col].tooltip = tooltip;
6891     },
6892
6893     /**
6894      * Returns the dataIndex for the specified column.
6895      * @param {Number} col The column index
6896      * @return {Number}
6897      */
6898     getDataIndex : function(col){
6899         return this.config[col].dataIndex;
6900     },
6901
6902     /**
6903      * Sets the dataIndex for a column.
6904      * @param {Number} col The column index
6905      * @param {Number} dataIndex The new dataIndex
6906      */
6907     setDataIndex : function(col, dataIndex){
6908         this.config[col].dataIndex = dataIndex;
6909     },
6910
6911     
6912     
6913     /**
6914      * Returns true if the cell is editable.
6915      * @param {Number} colIndex The column index
6916      * @param {Number} rowIndex The row index - this is nto actually used..?
6917      * @return {Boolean}
6918      */
6919     isCellEditable : function(colIndex, rowIndex){
6920         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6921     },
6922
6923     /**
6924      * Returns the editor defined for the cell/column.
6925      * return false or null to disable editing.
6926      * @param {Number} colIndex The column index
6927      * @param {Number} rowIndex The row index
6928      * @return {Object}
6929      */
6930     getCellEditor : function(colIndex, rowIndex){
6931         return this.config[colIndex].editor;
6932     },
6933
6934     /**
6935      * Sets if a column is editable.
6936      * @param {Number} col The column index
6937      * @param {Boolean} editable True if the column is editable
6938      */
6939     setEditable : function(col, editable){
6940         this.config[col].editable = editable;
6941     },
6942
6943
6944     /**
6945      * Returns true if the column is hidden.
6946      * @param {Number} colIndex The column index
6947      * @return {Boolean}
6948      */
6949     isHidden : function(colIndex){
6950         return this.config[colIndex].hidden;
6951     },
6952
6953
6954     /**
6955      * Returns true if the column width cannot be changed
6956      */
6957     isFixed : function(colIndex){
6958         return this.config[colIndex].fixed;
6959     },
6960
6961     /**
6962      * Returns true if the column can be resized
6963      * @return {Boolean}
6964      */
6965     isResizable : function(colIndex){
6966         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6967     },
6968     /**
6969      * Sets if a column is hidden.
6970      * @param {Number} colIndex The column index
6971      * @param {Boolean} hidden True if the column is hidden
6972      */
6973     setHidden : function(colIndex, hidden){
6974         this.config[colIndex].hidden = hidden;
6975         this.totalWidth = null;
6976         this.fireEvent("hiddenchange", this, colIndex, hidden);
6977     },
6978
6979     /**
6980      * Sets the editor for a column.
6981      * @param {Number} col The column index
6982      * @param {Object} editor The editor object
6983      */
6984     setEditor : function(col, editor){
6985         this.config[col].editor = editor;
6986     }
6987 });
6988
6989 Roo.grid.ColumnModel.defaultRenderer = function(value)
6990 {
6991     if(typeof value == "object") {
6992         return value;
6993     }
6994         if(typeof value == "string" && value.length < 1){
6995             return "&#160;";
6996         }
6997     
6998         return String.format("{0}", value);
6999 };
7000
7001 // Alias for backwards compatibility
7002 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7003 /*
7004  * Based on:
7005  * Ext JS Library 1.1.1
7006  * Copyright(c) 2006-2007, Ext JS, LLC.
7007  *
7008  * Originally Released Under LGPL - original licence link has changed is not relivant.
7009  *
7010  * Fork - LGPL
7011  * <script type="text/javascript">
7012  */
7013  
7014 /**
7015  * @class Roo.LoadMask
7016  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7017  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7018  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7019  * element's UpdateManager load indicator and will be destroyed after the initial load.
7020  * @constructor
7021  * Create a new LoadMask
7022  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7023  * @param {Object} config The config object
7024  */
7025 Roo.LoadMask = function(el, config){
7026     this.el = Roo.get(el);
7027     Roo.apply(this, config);
7028     if(this.store){
7029         this.store.on('beforeload', this.onBeforeLoad, this);
7030         this.store.on('load', this.onLoad, this);
7031         this.store.on('loadexception', this.onLoadException, this);
7032         this.removeMask = false;
7033     }else{
7034         var um = this.el.getUpdateManager();
7035         um.showLoadIndicator = false; // disable the default indicator
7036         um.on('beforeupdate', this.onBeforeLoad, this);
7037         um.on('update', this.onLoad, this);
7038         um.on('failure', this.onLoad, this);
7039         this.removeMask = true;
7040     }
7041 };
7042
7043 Roo.LoadMask.prototype = {
7044     /**
7045      * @cfg {Boolean} removeMask
7046      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7047      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7048      */
7049     /**
7050      * @cfg {String} msg
7051      * The text to display in a centered loading message box (defaults to 'Loading...')
7052      */
7053     msg : 'Loading...',
7054     /**
7055      * @cfg {String} msgCls
7056      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7057      */
7058     msgCls : 'x-mask-loading',
7059
7060     /**
7061      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7062      * @type Boolean
7063      */
7064     disabled: false,
7065
7066     /**
7067      * Disables the mask to prevent it from being displayed
7068      */
7069     disable : function(){
7070        this.disabled = true;
7071     },
7072
7073     /**
7074      * Enables the mask so that it can be displayed
7075      */
7076     enable : function(){
7077         this.disabled = false;
7078     },
7079     
7080     onLoadException : function()
7081     {
7082         Roo.log(arguments);
7083         
7084         if (typeof(arguments[3]) != 'undefined') {
7085             Roo.MessageBox.alert("Error loading",arguments[3]);
7086         } 
7087         /*
7088         try {
7089             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7090                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7091             }   
7092         } catch(e) {
7093             
7094         }
7095         */
7096     
7097         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7098     },
7099     // private
7100     onLoad : function()
7101     {
7102         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7103     },
7104
7105     // private
7106     onBeforeLoad : function(){
7107         if(!this.disabled){
7108             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7109         }
7110     },
7111
7112     // private
7113     destroy : function(){
7114         if(this.store){
7115             this.store.un('beforeload', this.onBeforeLoad, this);
7116             this.store.un('load', this.onLoad, this);
7117             this.store.un('loadexception', this.onLoadException, this);
7118         }else{
7119             var um = this.el.getUpdateManager();
7120             um.un('beforeupdate', this.onBeforeLoad, this);
7121             um.un('update', this.onLoad, this);
7122             um.un('failure', this.onLoad, this);
7123         }
7124     }
7125 };/*
7126  * - LGPL
7127  *
7128  * table
7129  * 
7130  */
7131
7132 /**
7133  * @class Roo.bootstrap.Table
7134  * @extends Roo.bootstrap.Component
7135  * Bootstrap Table class
7136  * @cfg {String} cls table class
7137  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7138  * @cfg {String} bgcolor Specifies the background color for a table
7139  * @cfg {Number} border Specifies whether the table cells should have borders or not
7140  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7141  * @cfg {Number} cellspacing Specifies the space between cells
7142  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7143  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7144  * @cfg {String} sortable Specifies that the table should be sortable
7145  * @cfg {String} summary Specifies a summary of the content of a table
7146  * @cfg {Number} width Specifies the width of a table
7147  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7148  * 
7149  * @cfg {boolean} striped Should the rows be alternative striped
7150  * @cfg {boolean} bordered Add borders to the table
7151  * @cfg {boolean} hover Add hover highlighting
7152  * @cfg {boolean} condensed Format condensed
7153  * @cfg {boolean} responsive Format condensed
7154  * @cfg {Boolean} loadMask (true|false) default false
7155  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7156  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7157  * @cfg {Boolean} rowSelection (true|false) default false
7158  * @cfg {Boolean} cellSelection (true|false) default false
7159  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7160  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7161  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7162  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7163  
7164  * 
7165  * @constructor
7166  * Create a new Table
7167  * @param {Object} config The config object
7168  */
7169
7170 Roo.bootstrap.Table = function(config){
7171     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7172     
7173   
7174     
7175     // BC...
7176     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7177     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7178     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7179     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7180     
7181     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7182     if (this.sm) {
7183         this.sm.grid = this;
7184         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7185         this.sm = this.selModel;
7186         this.sm.xmodule = this.xmodule || false;
7187     }
7188     
7189     if (this.cm && typeof(this.cm.config) == 'undefined') {
7190         this.colModel = new Roo.grid.ColumnModel(this.cm);
7191         this.cm = this.colModel;
7192         this.cm.xmodule = this.xmodule || false;
7193     }
7194     if (this.store) {
7195         this.store= Roo.factory(this.store, Roo.data);
7196         this.ds = this.store;
7197         this.ds.xmodule = this.xmodule || false;
7198          
7199     }
7200     if (this.footer && this.store) {
7201         this.footer.dataSource = this.ds;
7202         this.footer = Roo.factory(this.footer);
7203     }
7204     
7205     /** @private */
7206     this.addEvents({
7207         /**
7208          * @event cellclick
7209          * Fires when a cell is clicked
7210          * @param {Roo.bootstrap.Table} this
7211          * @param {Roo.Element} el
7212          * @param {Number} rowIndex
7213          * @param {Number} columnIndex
7214          * @param {Roo.EventObject} e
7215          */
7216         "cellclick" : true,
7217         /**
7218          * @event celldblclick
7219          * Fires when a cell is double clicked
7220          * @param {Roo.bootstrap.Table} this
7221          * @param {Roo.Element} el
7222          * @param {Number} rowIndex
7223          * @param {Number} columnIndex
7224          * @param {Roo.EventObject} e
7225          */
7226         "celldblclick" : true,
7227         /**
7228          * @event rowclick
7229          * Fires when a row is clicked
7230          * @param {Roo.bootstrap.Table} this
7231          * @param {Roo.Element} el
7232          * @param {Number} rowIndex
7233          * @param {Roo.EventObject} e
7234          */
7235         "rowclick" : true,
7236         /**
7237          * @event rowdblclick
7238          * Fires when a row is double clicked
7239          * @param {Roo.bootstrap.Table} this
7240          * @param {Roo.Element} el
7241          * @param {Number} rowIndex
7242          * @param {Roo.EventObject} e
7243          */
7244         "rowdblclick" : true,
7245         /**
7246          * @event mouseover
7247          * Fires when a mouseover occur
7248          * @param {Roo.bootstrap.Table} this
7249          * @param {Roo.Element} el
7250          * @param {Number} rowIndex
7251          * @param {Number} columnIndex
7252          * @param {Roo.EventObject} e
7253          */
7254         "mouseover" : true,
7255         /**
7256          * @event mouseout
7257          * Fires when a mouseout occur
7258          * @param {Roo.bootstrap.Table} this
7259          * @param {Roo.Element} el
7260          * @param {Number} rowIndex
7261          * @param {Number} columnIndex
7262          * @param {Roo.EventObject} e
7263          */
7264         "mouseout" : true,
7265         /**
7266          * @event rowclass
7267          * Fires when a row is rendered, so you can change add a style to it.
7268          * @param {Roo.bootstrap.Table} this
7269          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7270          */
7271         'rowclass' : true,
7272           /**
7273          * @event rowsrendered
7274          * Fires when all the  rows have been rendered
7275          * @param {Roo.bootstrap.Table} this
7276          */
7277         'rowsrendered' : true,
7278         /**
7279          * @event contextmenu
7280          * The raw contextmenu event for the entire grid.
7281          * @param {Roo.EventObject} e
7282          */
7283         "contextmenu" : true,
7284         /**
7285          * @event rowcontextmenu
7286          * Fires when a row is right clicked
7287          * @param {Roo.bootstrap.Table} this
7288          * @param {Number} rowIndex
7289          * @param {Roo.EventObject} e
7290          */
7291         "rowcontextmenu" : true,
7292         /**
7293          * @event cellcontextmenu
7294          * Fires when a cell is right clicked
7295          * @param {Roo.bootstrap.Table} this
7296          * @param {Number} rowIndex
7297          * @param {Number} cellIndex
7298          * @param {Roo.EventObject} e
7299          */
7300          "cellcontextmenu" : true,
7301          /**
7302          * @event headercontextmenu
7303          * Fires when a header is right clicked
7304          * @param {Roo.bootstrap.Table} this
7305          * @param {Number} columnIndex
7306          * @param {Roo.EventObject} e
7307          */
7308         "headercontextmenu" : true
7309     });
7310 };
7311
7312 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7313     
7314     cls: false,
7315     align: false,
7316     bgcolor: false,
7317     border: false,
7318     cellpadding: false,
7319     cellspacing: false,
7320     frame: false,
7321     rules: false,
7322     sortable: false,
7323     summary: false,
7324     width: false,
7325     striped : false,
7326     scrollBody : false,
7327     bordered: false,
7328     hover:  false,
7329     condensed : false,
7330     responsive : false,
7331     sm : false,
7332     cm : false,
7333     store : false,
7334     loadMask : false,
7335     footerShow : true,
7336     headerShow : true,
7337   
7338     rowSelection : false,
7339     cellSelection : false,
7340     layout : false,
7341     
7342     // Roo.Element - the tbody
7343     mainBody: false,
7344     // Roo.Element - thead element
7345     mainHead: false,
7346     
7347     container: false, // used by gridpanel...
7348     
7349     lazyLoad : false,
7350     
7351     CSS : Roo.util.CSS,
7352     
7353     auto_hide_footer : false,
7354     
7355     getAutoCreate : function()
7356     {
7357         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7358         
7359         cfg = {
7360             tag: 'table',
7361             cls : 'table',
7362             cn : []
7363         };
7364         if (this.scrollBody) {
7365             cfg.cls += ' table-body-fixed';
7366         }    
7367         if (this.striped) {
7368             cfg.cls += ' table-striped';
7369         }
7370         
7371         if (this.hover) {
7372             cfg.cls += ' table-hover';
7373         }
7374         if (this.bordered) {
7375             cfg.cls += ' table-bordered';
7376         }
7377         if (this.condensed) {
7378             cfg.cls += ' table-condensed';
7379         }
7380         if (this.responsive) {
7381             cfg.cls += ' table-responsive';
7382         }
7383         
7384         if (this.cls) {
7385             cfg.cls+=  ' ' +this.cls;
7386         }
7387         
7388         // this lot should be simplifed...
7389         var _t = this;
7390         var cp = [
7391             'align',
7392             'bgcolor',
7393             'border',
7394             'cellpadding',
7395             'cellspacing',
7396             'frame',
7397             'rules',
7398             'sortable',
7399             'summary',
7400             'width'
7401         ].forEach(function(k) {
7402             if (_t[k]) {
7403                 cfg[k] = _t[k];
7404             }
7405         });
7406         
7407         
7408         if (this.layout) {
7409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7410         }
7411         
7412         if(this.store || this.cm){
7413             if(this.headerShow){
7414                 cfg.cn.push(this.renderHeader());
7415             }
7416             
7417             cfg.cn.push(this.renderBody());
7418             
7419             if(this.footerShow){
7420                 cfg.cn.push(this.renderFooter());
7421             }
7422             // where does this come from?
7423             //cfg.cls+=  ' TableGrid';
7424         }
7425         
7426         return { cn : [ cfg ] };
7427     },
7428     
7429     initEvents : function()
7430     {   
7431         if(!this.store || !this.cm){
7432             return;
7433         }
7434         if (this.selModel) {
7435             this.selModel.initEvents();
7436         }
7437         
7438         
7439         //Roo.log('initEvents with ds!!!!');
7440         
7441         this.mainBody = this.el.select('tbody', true).first();
7442         this.mainHead = this.el.select('thead', true).first();
7443         this.mainFoot = this.el.select('tfoot', true).first();
7444         
7445         
7446         
7447         var _this = this;
7448         
7449         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7450             e.on('click', _this.sort, _this);
7451         });
7452         
7453         this.mainBody.on("click", this.onClick, this);
7454         this.mainBody.on("dblclick", this.onDblClick, this);
7455         
7456         // why is this done????? = it breaks dialogs??
7457         //this.parent().el.setStyle('position', 'relative');
7458         
7459         
7460         if (this.footer) {
7461             this.footer.parentId = this.id;
7462             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7463             
7464             if(this.lazyLoad){
7465                 this.el.select('tfoot tr td').first().addClass('hide');
7466             }
7467         } 
7468         
7469         if(this.loadMask) {
7470             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7471         }
7472         
7473         this.store.on('load', this.onLoad, this);
7474         this.store.on('beforeload', this.onBeforeLoad, this);
7475         this.store.on('update', this.onUpdate, this);
7476         this.store.on('add', this.onAdd, this);
7477         this.store.on("clear", this.clear, this);
7478         
7479         this.el.on("contextmenu", this.onContextMenu, this);
7480         
7481         this.mainBody.on('scroll', this.onBodyScroll, this);
7482         
7483         this.cm.on("headerchange", this.onHeaderChange, this);
7484         
7485         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7486         
7487     },
7488     
7489     onContextMenu : function(e, t)
7490     {
7491         this.processEvent("contextmenu", e);
7492     },
7493     
7494     processEvent : function(name, e)
7495     {
7496         if (name != 'touchstart' ) {
7497             this.fireEvent(name, e);    
7498         }
7499         
7500         var t = e.getTarget();
7501         
7502         var cell = Roo.get(t);
7503         
7504         if(!cell){
7505             return;
7506         }
7507         
7508         if(cell.findParent('tfoot', false, true)){
7509             return;
7510         }
7511         
7512         if(cell.findParent('thead', false, true)){
7513             
7514             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7515                 cell = Roo.get(t).findParent('th', false, true);
7516                 if (!cell) {
7517                     Roo.log("failed to find th in thead?");
7518                     Roo.log(e.getTarget());
7519                     return;
7520                 }
7521             }
7522             
7523             var cellIndex = cell.dom.cellIndex;
7524             
7525             var ename = name == 'touchstart' ? 'click' : name;
7526             this.fireEvent("header" + ename, this, cellIndex, e);
7527             
7528             return;
7529         }
7530         
7531         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7532             cell = Roo.get(t).findParent('td', false, true);
7533             if (!cell) {
7534                 Roo.log("failed to find th in tbody?");
7535                 Roo.log(e.getTarget());
7536                 return;
7537             }
7538         }
7539         
7540         var row = cell.findParent('tr', false, true);
7541         var cellIndex = cell.dom.cellIndex;
7542         var rowIndex = row.dom.rowIndex - 1;
7543         
7544         if(row !== false){
7545             
7546             this.fireEvent("row" + name, this, rowIndex, e);
7547             
7548             if(cell !== false){
7549             
7550                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7551             }
7552         }
7553         
7554     },
7555     
7556     onMouseover : function(e, el)
7557     {
7558         var cell = Roo.get(el);
7559         
7560         if(!cell){
7561             return;
7562         }
7563         
7564         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7565             cell = cell.findParent('td', false, true);
7566         }
7567         
7568         var row = cell.findParent('tr', false, true);
7569         var cellIndex = cell.dom.cellIndex;
7570         var rowIndex = row.dom.rowIndex - 1; // start from 0
7571         
7572         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7573         
7574     },
7575     
7576     onMouseout : function(e, el)
7577     {
7578         var cell = Roo.get(el);
7579         
7580         if(!cell){
7581             return;
7582         }
7583         
7584         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7585             cell = cell.findParent('td', false, true);
7586         }
7587         
7588         var row = cell.findParent('tr', false, true);
7589         var cellIndex = cell.dom.cellIndex;
7590         var rowIndex = row.dom.rowIndex - 1; // start from 0
7591         
7592         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7593         
7594     },
7595     
7596     onClick : function(e, el)
7597     {
7598         var cell = Roo.get(el);
7599         
7600         if(!cell || (!this.cellSelection && !this.rowSelection)){
7601             return;
7602         }
7603         
7604         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7605             cell = cell.findParent('td', false, true);
7606         }
7607         
7608         if(!cell || typeof(cell) == 'undefined'){
7609             return;
7610         }
7611         
7612         var row = cell.findParent('tr', false, true);
7613         
7614         if(!row || typeof(row) == 'undefined'){
7615             return;
7616         }
7617         
7618         var cellIndex = cell.dom.cellIndex;
7619         var rowIndex = this.getRowIndex(row);
7620         
7621         // why??? - should these not be based on SelectionModel?
7622         if(this.cellSelection){
7623             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7624         }
7625         
7626         if(this.rowSelection){
7627             this.fireEvent('rowclick', this, row, rowIndex, e);
7628         }
7629         
7630         
7631     },
7632         
7633     onDblClick : function(e,el)
7634     {
7635         var cell = Roo.get(el);
7636         
7637         if(!cell || (!this.cellSelection && !this.rowSelection)){
7638             return;
7639         }
7640         
7641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7642             cell = cell.findParent('td', false, true);
7643         }
7644         
7645         if(!cell || typeof(cell) == 'undefined'){
7646             return;
7647         }
7648         
7649         var row = cell.findParent('tr', false, true);
7650         
7651         if(!row || typeof(row) == 'undefined'){
7652             return;
7653         }
7654         
7655         var cellIndex = cell.dom.cellIndex;
7656         var rowIndex = this.getRowIndex(row);
7657         
7658         if(this.cellSelection){
7659             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7660         }
7661         
7662         if(this.rowSelection){
7663             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7664         }
7665     },
7666     
7667     sort : function(e,el)
7668     {
7669         var col = Roo.get(el);
7670         
7671         if(!col.hasClass('sortable')){
7672             return;
7673         }
7674         
7675         var sort = col.attr('sort');
7676         var dir = 'ASC';
7677         
7678         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7679             dir = 'DESC';
7680         }
7681         
7682         this.store.sortInfo = {field : sort, direction : dir};
7683         
7684         if (this.footer) {
7685             Roo.log("calling footer first");
7686             this.footer.onClick('first');
7687         } else {
7688         
7689             this.store.load({ params : { start : 0 } });
7690         }
7691     },
7692     
7693     renderHeader : function()
7694     {
7695         var header = {
7696             tag: 'thead',
7697             cn : []
7698         };
7699         
7700         var cm = this.cm;
7701         this.totalWidth = 0;
7702         
7703         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7704             
7705             var config = cm.config[i];
7706             
7707             var c = {
7708                 tag: 'th',
7709                 cls : 'x-hcol-' + i,
7710                 style : '',
7711                 html: cm.getColumnHeader(i)
7712             };
7713             
7714             var hh = '';
7715             
7716             if(typeof(config.sortable) != 'undefined' && config.sortable){
7717                 c.cls = 'sortable';
7718                 c.html = '<i class="glyphicon"></i>' + c.html;
7719             }
7720             
7721             // could use BS4 hidden-..-down 
7722             
7723             if(typeof(config.lgHeader) != 'undefined'){
7724                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7725             }
7726             
7727             if(typeof(config.mdHeader) != 'undefined'){
7728                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7729             }
7730             
7731             if(typeof(config.smHeader) != 'undefined'){
7732                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7733             }
7734             
7735             if(typeof(config.xsHeader) != 'undefined'){
7736                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7737             }
7738             
7739             if(hh.length){
7740                 c.html = hh;
7741             }
7742             
7743             if(typeof(config.tooltip) != 'undefined'){
7744                 c.tooltip = config.tooltip;
7745             }
7746             
7747             if(typeof(config.colspan) != 'undefined'){
7748                 c.colspan = config.colspan;
7749             }
7750             
7751             if(typeof(config.hidden) != 'undefined' && config.hidden){
7752                 c.style += ' display:none;';
7753             }
7754             
7755             if(typeof(config.dataIndex) != 'undefined'){
7756                 c.sort = config.dataIndex;
7757             }
7758             
7759            
7760             
7761             if(typeof(config.align) != 'undefined' && config.align.length){
7762                 c.style += ' text-align:' + config.align + ';';
7763             }
7764             
7765             if(typeof(config.width) != 'undefined'){
7766                 c.style += ' width:' + config.width + 'px;';
7767                 this.totalWidth += config.width;
7768             } else {
7769                 this.totalWidth += 100; // assume minimum of 100 per column?
7770             }
7771             
7772             if(typeof(config.cls) != 'undefined'){
7773                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7774             }
7775             
7776             ['xs','sm','md','lg'].map(function(size){
7777                 
7778                 if(typeof(config[size]) == 'undefined'){
7779                     return;
7780                 }
7781                  
7782                 if (!config[size]) { // 0 = hidden
7783                     // BS 4 '0' is treated as hide that column and below.
7784                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7785                     return;
7786                 }
7787                 
7788                 c.cls += ' col-' + size + '-' + config[size] + (
7789                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7790                 );
7791                 
7792                 
7793             });
7794             
7795             header.cn.push(c)
7796         }
7797         
7798         return header;
7799     },
7800     
7801     renderBody : function()
7802     {
7803         var body = {
7804             tag: 'tbody',
7805             cn : [
7806                 {
7807                     tag: 'tr',
7808                     cn : [
7809                         {
7810                             tag : 'td',
7811                             colspan :  this.cm.getColumnCount()
7812                         }
7813                     ]
7814                 }
7815             ]
7816         };
7817         
7818         return body;
7819     },
7820     
7821     renderFooter : function()
7822     {
7823         var footer = {
7824             tag: 'tfoot',
7825             cn : [
7826                 {
7827                     tag: 'tr',
7828                     cn : [
7829                         {
7830                             tag : 'td',
7831                             colspan :  this.cm.getColumnCount()
7832                         }
7833                     ]
7834                 }
7835             ]
7836         };
7837         
7838         return footer;
7839     },
7840     
7841     
7842     
7843     onLoad : function()
7844     {
7845 //        Roo.log('ds onload');
7846         this.clear();
7847         
7848         var _this = this;
7849         var cm = this.cm;
7850         var ds = this.store;
7851         
7852         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7853             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7854             if (_this.store.sortInfo) {
7855                     
7856                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7857                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7858                 }
7859                 
7860                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7861                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7862                 }
7863             }
7864         });
7865         
7866         var tbody =  this.mainBody;
7867               
7868         if(ds.getCount() > 0){
7869             ds.data.each(function(d,rowIndex){
7870                 var row =  this.renderRow(cm, ds, rowIndex);
7871                 
7872                 tbody.createChild(row);
7873                 
7874                 var _this = this;
7875                 
7876                 if(row.cellObjects.length){
7877                     Roo.each(row.cellObjects, function(r){
7878                         _this.renderCellObject(r);
7879                     })
7880                 }
7881                 
7882             }, this);
7883         }
7884         
7885         var tfoot = this.el.select('tfoot', true).first();
7886         
7887         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7888             
7889             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7890             
7891             var total = this.ds.getTotalCount();
7892             
7893             if(this.footer.pageSize < total){
7894                 this.mainFoot.show();
7895             }
7896         }
7897         
7898         Roo.each(this.el.select('tbody td', true).elements, function(e){
7899             e.on('mouseover', _this.onMouseover, _this);
7900         });
7901         
7902         Roo.each(this.el.select('tbody td', true).elements, function(e){
7903             e.on('mouseout', _this.onMouseout, _this);
7904         });
7905         this.fireEvent('rowsrendered', this);
7906         
7907         this.autoSize();
7908     },
7909     
7910     
7911     onUpdate : function(ds,record)
7912     {
7913         this.refreshRow(record);
7914         this.autoSize();
7915     },
7916     
7917     onRemove : function(ds, record, index, isUpdate){
7918         if(isUpdate !== true){
7919             this.fireEvent("beforerowremoved", this, index, record);
7920         }
7921         var bt = this.mainBody.dom;
7922         
7923         var rows = this.el.select('tbody > tr', true).elements;
7924         
7925         if(typeof(rows[index]) != 'undefined'){
7926             bt.removeChild(rows[index].dom);
7927         }
7928         
7929 //        if(bt.rows[index]){
7930 //            bt.removeChild(bt.rows[index]);
7931 //        }
7932         
7933         if(isUpdate !== true){
7934             //this.stripeRows(index);
7935             //this.syncRowHeights(index, index);
7936             //this.layout();
7937             this.fireEvent("rowremoved", this, index, record);
7938         }
7939     },
7940     
7941     onAdd : function(ds, records, rowIndex)
7942     {
7943         //Roo.log('on Add called');
7944         // - note this does not handle multiple adding very well..
7945         var bt = this.mainBody.dom;
7946         for (var i =0 ; i < records.length;i++) {
7947             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7948             //Roo.log(records[i]);
7949             //Roo.log(this.store.getAt(rowIndex+i));
7950             this.insertRow(this.store, rowIndex + i, false);
7951             return;
7952         }
7953         
7954     },
7955     
7956     
7957     refreshRow : function(record){
7958         var ds = this.store, index;
7959         if(typeof record == 'number'){
7960             index = record;
7961             record = ds.getAt(index);
7962         }else{
7963             index = ds.indexOf(record);
7964         }
7965         this.insertRow(ds, index, true);
7966         this.autoSize();
7967         this.onRemove(ds, record, index+1, true);
7968         this.autoSize();
7969         //this.syncRowHeights(index, index);
7970         //this.layout();
7971         this.fireEvent("rowupdated", this, index, record);
7972     },
7973     
7974     insertRow : function(dm, rowIndex, isUpdate){
7975         
7976         if(!isUpdate){
7977             this.fireEvent("beforerowsinserted", this, rowIndex);
7978         }
7979             //var s = this.getScrollState();
7980         var row = this.renderRow(this.cm, this.store, rowIndex);
7981         // insert before rowIndex..
7982         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7983         
7984         var _this = this;
7985                 
7986         if(row.cellObjects.length){
7987             Roo.each(row.cellObjects, function(r){
7988                 _this.renderCellObject(r);
7989             })
7990         }
7991             
7992         if(!isUpdate){
7993             this.fireEvent("rowsinserted", this, rowIndex);
7994             //this.syncRowHeights(firstRow, lastRow);
7995             //this.stripeRows(firstRow);
7996             //this.layout();
7997         }
7998         
7999     },
8000     
8001     
8002     getRowDom : function(rowIndex)
8003     {
8004         var rows = this.el.select('tbody > tr', true).elements;
8005         
8006         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8007         
8008     },
8009     // returns the object tree for a tr..
8010   
8011     
8012     renderRow : function(cm, ds, rowIndex) 
8013     {
8014         var d = ds.getAt(rowIndex);
8015         
8016         var row = {
8017             tag : 'tr',
8018             cls : 'x-row-' + rowIndex,
8019             cn : []
8020         };
8021             
8022         var cellObjects = [];
8023         
8024         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8025             var config = cm.config[i];
8026             
8027             var renderer = cm.getRenderer(i);
8028             var value = '';
8029             var id = false;
8030             
8031             if(typeof(renderer) !== 'undefined'){
8032                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8033             }
8034             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8035             // and are rendered into the cells after the row is rendered - using the id for the element.
8036             
8037             if(typeof(value) === 'object'){
8038                 id = Roo.id();
8039                 cellObjects.push({
8040                     container : id,
8041                     cfg : value 
8042                 })
8043             }
8044             
8045             var rowcfg = {
8046                 record: d,
8047                 rowIndex : rowIndex,
8048                 colIndex : i,
8049                 rowClass : ''
8050             };
8051
8052             this.fireEvent('rowclass', this, rowcfg);
8053             
8054             var td = {
8055                 tag: 'td',
8056                 cls : rowcfg.rowClass + ' x-col-' + i,
8057                 style: '',
8058                 html: (typeof(value) === 'object') ? '' : value
8059             };
8060             
8061             if (id) {
8062                 td.id = id;
8063             }
8064             
8065             if(typeof(config.colspan) != 'undefined'){
8066                 td.colspan = config.colspan;
8067             }
8068             
8069             if(typeof(config.hidden) != 'undefined' && config.hidden){
8070                 td.style += ' display:none;';
8071             }
8072             
8073             if(typeof(config.align) != 'undefined' && config.align.length){
8074                 td.style += ' text-align:' + config.align + ';';
8075             }
8076             if(typeof(config.valign) != 'undefined' && config.valign.length){
8077                 td.style += ' vertical-align:' + config.valign + ';';
8078             }
8079             
8080             if(typeof(config.width) != 'undefined'){
8081                 td.style += ' width:' +  config.width + 'px;';
8082             }
8083             
8084             if(typeof(config.cursor) != 'undefined'){
8085                 td.style += ' cursor:' +  config.cursor + ';';
8086             }
8087             
8088             if(typeof(config.cls) != 'undefined'){
8089                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8090             }
8091             
8092             ['xs','sm','md','lg'].map(function(size){
8093                 
8094                 if(typeof(config[size]) == 'undefined'){
8095                     return;
8096                 }
8097                 
8098                 
8099                   
8100                 if (!config[size]) { // 0 = hidden
8101                     // BS 4 '0' is treated as hide that column and below.
8102                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8103                     return;
8104                 }
8105                 
8106                 td.cls += ' col-' + size + '-' + config[size] + (
8107                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8108                 );
8109                  
8110
8111             });
8112             
8113             row.cn.push(td);
8114            
8115         }
8116         
8117         row.cellObjects = cellObjects;
8118         
8119         return row;
8120           
8121     },
8122     
8123     
8124     
8125     onBeforeLoad : function()
8126     {
8127         
8128     },
8129      /**
8130      * Remove all rows
8131      */
8132     clear : function()
8133     {
8134         this.el.select('tbody', true).first().dom.innerHTML = '';
8135     },
8136     /**
8137      * Show or hide a row.
8138      * @param {Number} rowIndex to show or hide
8139      * @param {Boolean} state hide
8140      */
8141     setRowVisibility : function(rowIndex, state)
8142     {
8143         var bt = this.mainBody.dom;
8144         
8145         var rows = this.el.select('tbody > tr', true).elements;
8146         
8147         if(typeof(rows[rowIndex]) == 'undefined'){
8148             return;
8149         }
8150         rows[rowIndex].dom.style.display = state ? '' : 'none';
8151     },
8152     
8153     
8154     getSelectionModel : function(){
8155         if(!this.selModel){
8156             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8157         }
8158         return this.selModel;
8159     },
8160     /*
8161      * Render the Roo.bootstrap object from renderder
8162      */
8163     renderCellObject : function(r)
8164     {
8165         var _this = this;
8166         
8167         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8168         
8169         var t = r.cfg.render(r.container);
8170         
8171         if(r.cfg.cn){
8172             Roo.each(r.cfg.cn, function(c){
8173                 var child = {
8174                     container: t.getChildContainer(),
8175                     cfg: c
8176                 };
8177                 _this.renderCellObject(child);
8178             })
8179         }
8180     },
8181     
8182     getRowIndex : function(row)
8183     {
8184         var rowIndex = -1;
8185         
8186         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8187             if(el != row){
8188                 return;
8189             }
8190             
8191             rowIndex = index;
8192         });
8193         
8194         return rowIndex;
8195     },
8196      /**
8197      * Returns the grid's underlying element = used by panel.Grid
8198      * @return {Element} The element
8199      */
8200     getGridEl : function(){
8201         return this.el;
8202     },
8203      /**
8204      * Forces a resize - used by panel.Grid
8205      * @return {Element} The element
8206      */
8207     autoSize : function()
8208     {
8209         //var ctr = Roo.get(this.container.dom.parentElement);
8210         var ctr = Roo.get(this.el.dom);
8211         
8212         var thd = this.getGridEl().select('thead',true).first();
8213         var tbd = this.getGridEl().select('tbody', true).first();
8214         var tfd = this.getGridEl().select('tfoot', true).first();
8215         
8216         var cw = ctr.getWidth();
8217         
8218         if (tbd) {
8219             
8220             tbd.setWidth(ctr.getWidth());
8221             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8222             // this needs fixing for various usage - currently only hydra job advers I think..
8223             //tdb.setHeight(
8224             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8225             //); 
8226             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8227             cw -= barsize;
8228         }
8229         cw = Math.max(cw, this.totalWidth);
8230         this.getGridEl().select('tr',true).setWidth(cw);
8231         // resize 'expandable coloumn?
8232         
8233         return; // we doe not have a view in this design..
8234         
8235     },
8236     onBodyScroll: function()
8237     {
8238         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8239         if(this.mainHead){
8240             this.mainHead.setStyle({
8241                 'position' : 'relative',
8242                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8243             });
8244         }
8245         
8246         if(this.lazyLoad){
8247             
8248             var scrollHeight = this.mainBody.dom.scrollHeight;
8249             
8250             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8251             
8252             var height = this.mainBody.getHeight();
8253             
8254             if(scrollHeight - height == scrollTop) {
8255                 
8256                 var total = this.ds.getTotalCount();
8257                 
8258                 if(this.footer.cursor + this.footer.pageSize < total){
8259                     
8260                     this.footer.ds.load({
8261                         params : {
8262                             start : this.footer.cursor + this.footer.pageSize,
8263                             limit : this.footer.pageSize
8264                         },
8265                         add : true
8266                     });
8267                 }
8268             }
8269             
8270         }
8271     },
8272     
8273     onHeaderChange : function()
8274     {
8275         var header = this.renderHeader();
8276         var table = this.el.select('table', true).first();
8277         
8278         this.mainHead.remove();
8279         this.mainHead = table.createChild(header, this.mainBody, false);
8280     },
8281     
8282     onHiddenChange : function(colModel, colIndex, hidden)
8283     {
8284         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8285         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8286         
8287         this.CSS.updateRule(thSelector, "display", "");
8288         this.CSS.updateRule(tdSelector, "display", "");
8289         
8290         if(hidden){
8291             this.CSS.updateRule(thSelector, "display", "none");
8292             this.CSS.updateRule(tdSelector, "display", "none");
8293         }
8294         
8295         this.onHeaderChange();
8296         this.onLoad();
8297     },
8298     
8299     setColumnWidth: function(col_index, width)
8300     {
8301         // width = "md-2 xs-2..."
8302         if(!this.colModel.config[col_index]) {
8303             return;
8304         }
8305         
8306         var w = width.split(" ");
8307         
8308         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8309         
8310         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8311         
8312         
8313         for(var j = 0; j < w.length; j++) {
8314             
8315             if(!w[j]) {
8316                 continue;
8317             }
8318             
8319             var size_cls = w[j].split("-");
8320             
8321             if(!Number.isInteger(size_cls[1] * 1)) {
8322                 continue;
8323             }
8324             
8325             if(!this.colModel.config[col_index][size_cls[0]]) {
8326                 continue;
8327             }
8328             
8329             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8330                 continue;
8331             }
8332             
8333             h_row[0].classList.replace(
8334                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8335                 "col-"+size_cls[0]+"-"+size_cls[1]
8336             );
8337             
8338             for(var i = 0; i < rows.length; i++) {
8339                 
8340                 var size_cls = w[j].split("-");
8341                 
8342                 if(!Number.isInteger(size_cls[1] * 1)) {
8343                     continue;
8344                 }
8345                 
8346                 if(!this.colModel.config[col_index][size_cls[0]]) {
8347                     continue;
8348                 }
8349                 
8350                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8351                     continue;
8352                 }
8353                 
8354                 rows[i].classList.replace(
8355                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8356                     "col-"+size_cls[0]+"-"+size_cls[1]
8357                 );
8358             }
8359             
8360             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8361         }
8362     }
8363 });
8364
8365  
8366
8367  /*
8368  * - LGPL
8369  *
8370  * table cell
8371  * 
8372  */
8373
8374 /**
8375  * @class Roo.bootstrap.TableCell
8376  * @extends Roo.bootstrap.Component
8377  * Bootstrap TableCell class
8378  * @cfg {String} html cell contain text
8379  * @cfg {String} cls cell class
8380  * @cfg {String} tag cell tag (td|th) default td
8381  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8382  * @cfg {String} align Aligns the content in a cell
8383  * @cfg {String} axis Categorizes cells
8384  * @cfg {String} bgcolor Specifies the background color of a cell
8385  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8386  * @cfg {Number} colspan Specifies the number of columns a cell should span
8387  * @cfg {String} headers Specifies one or more header cells a cell is related to
8388  * @cfg {Number} height Sets the height of a cell
8389  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8390  * @cfg {Number} rowspan Sets the number of rows a cell should span
8391  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8392  * @cfg {String} valign Vertical aligns the content in a cell
8393  * @cfg {Number} width Specifies the width of a cell
8394  * 
8395  * @constructor
8396  * Create a new TableCell
8397  * @param {Object} config The config object
8398  */
8399
8400 Roo.bootstrap.TableCell = function(config){
8401     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8402 };
8403
8404 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8405     
8406     html: false,
8407     cls: false,
8408     tag: false,
8409     abbr: false,
8410     align: false,
8411     axis: false,
8412     bgcolor: false,
8413     charoff: false,
8414     colspan: false,
8415     headers: false,
8416     height: false,
8417     nowrap: false,
8418     rowspan: false,
8419     scope: false,
8420     valign: false,
8421     width: false,
8422     
8423     
8424     getAutoCreate : function(){
8425         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8426         
8427         cfg = {
8428             tag: 'td'
8429         };
8430         
8431         if(this.tag){
8432             cfg.tag = this.tag;
8433         }
8434         
8435         if (this.html) {
8436             cfg.html=this.html
8437         }
8438         if (this.cls) {
8439             cfg.cls=this.cls
8440         }
8441         if (this.abbr) {
8442             cfg.abbr=this.abbr
8443         }
8444         if (this.align) {
8445             cfg.align=this.align
8446         }
8447         if (this.axis) {
8448             cfg.axis=this.axis
8449         }
8450         if (this.bgcolor) {
8451             cfg.bgcolor=this.bgcolor
8452         }
8453         if (this.charoff) {
8454             cfg.charoff=this.charoff
8455         }
8456         if (this.colspan) {
8457             cfg.colspan=this.colspan
8458         }
8459         if (this.headers) {
8460             cfg.headers=this.headers
8461         }
8462         if (this.height) {
8463             cfg.height=this.height
8464         }
8465         if (this.nowrap) {
8466             cfg.nowrap=this.nowrap
8467         }
8468         if (this.rowspan) {
8469             cfg.rowspan=this.rowspan
8470         }
8471         if (this.scope) {
8472             cfg.scope=this.scope
8473         }
8474         if (this.valign) {
8475             cfg.valign=this.valign
8476         }
8477         if (this.width) {
8478             cfg.width=this.width
8479         }
8480         
8481         
8482         return cfg;
8483     }
8484    
8485 });
8486
8487  
8488
8489  /*
8490  * - LGPL
8491  *
8492  * table row
8493  * 
8494  */
8495
8496 /**
8497  * @class Roo.bootstrap.TableRow
8498  * @extends Roo.bootstrap.Component
8499  * Bootstrap TableRow class
8500  * @cfg {String} cls row class
8501  * @cfg {String} align Aligns the content in a table row
8502  * @cfg {String} bgcolor Specifies a background color for a table row
8503  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8504  * @cfg {String} valign Vertical aligns the content in a table row
8505  * 
8506  * @constructor
8507  * Create a new TableRow
8508  * @param {Object} config The config object
8509  */
8510
8511 Roo.bootstrap.TableRow = function(config){
8512     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8513 };
8514
8515 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8516     
8517     cls: false,
8518     align: false,
8519     bgcolor: false,
8520     charoff: false,
8521     valign: false,
8522     
8523     getAutoCreate : function(){
8524         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8525         
8526         cfg = {
8527             tag: 'tr'
8528         };
8529             
8530         if(this.cls){
8531             cfg.cls = this.cls;
8532         }
8533         if(this.align){
8534             cfg.align = this.align;
8535         }
8536         if(this.bgcolor){
8537             cfg.bgcolor = this.bgcolor;
8538         }
8539         if(this.charoff){
8540             cfg.charoff = this.charoff;
8541         }
8542         if(this.valign){
8543             cfg.valign = this.valign;
8544         }
8545         
8546         return cfg;
8547     }
8548    
8549 });
8550
8551  
8552
8553  /*
8554  * - LGPL
8555  *
8556  * table body
8557  * 
8558  */
8559
8560 /**
8561  * @class Roo.bootstrap.TableBody
8562  * @extends Roo.bootstrap.Component
8563  * Bootstrap TableBody class
8564  * @cfg {String} cls element class
8565  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8566  * @cfg {String} align Aligns the content inside the element
8567  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8568  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8569  * 
8570  * @constructor
8571  * Create a new TableBody
8572  * @param {Object} config The config object
8573  */
8574
8575 Roo.bootstrap.TableBody = function(config){
8576     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8577 };
8578
8579 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8580     
8581     cls: false,
8582     tag: false,
8583     align: false,
8584     charoff: false,
8585     valign: false,
8586     
8587     getAutoCreate : function(){
8588         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8589         
8590         cfg = {
8591             tag: 'tbody'
8592         };
8593             
8594         if (this.cls) {
8595             cfg.cls=this.cls
8596         }
8597         if(this.tag){
8598             cfg.tag = this.tag;
8599         }
8600         
8601         if(this.align){
8602             cfg.align = this.align;
8603         }
8604         if(this.charoff){
8605             cfg.charoff = this.charoff;
8606         }
8607         if(this.valign){
8608             cfg.valign = this.valign;
8609         }
8610         
8611         return cfg;
8612     }
8613     
8614     
8615 //    initEvents : function()
8616 //    {
8617 //        
8618 //        if(!this.store){
8619 //            return;
8620 //        }
8621 //        
8622 //        this.store = Roo.factory(this.store, Roo.data);
8623 //        this.store.on('load', this.onLoad, this);
8624 //        
8625 //        this.store.load();
8626 //        
8627 //    },
8628 //    
8629 //    onLoad: function () 
8630 //    {   
8631 //        this.fireEvent('load', this);
8632 //    }
8633 //    
8634 //   
8635 });
8636
8637  
8638
8639  /*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650 // as we use this in bootstrap.
8651 Roo.namespace('Roo.form');
8652  /**
8653  * @class Roo.form.Action
8654  * Internal Class used to handle form actions
8655  * @constructor
8656  * @param {Roo.form.BasicForm} el The form element or its id
8657  * @param {Object} config Configuration options
8658  */
8659
8660  
8661  
8662 // define the action interface
8663 Roo.form.Action = function(form, options){
8664     this.form = form;
8665     this.options = options || {};
8666 };
8667 /**
8668  * Client Validation Failed
8669  * @const 
8670  */
8671 Roo.form.Action.CLIENT_INVALID = 'client';
8672 /**
8673  * Server Validation Failed
8674  * @const 
8675  */
8676 Roo.form.Action.SERVER_INVALID = 'server';
8677  /**
8678  * Connect to Server Failed
8679  * @const 
8680  */
8681 Roo.form.Action.CONNECT_FAILURE = 'connect';
8682 /**
8683  * Reading Data from Server Failed
8684  * @const 
8685  */
8686 Roo.form.Action.LOAD_FAILURE = 'load';
8687
8688 Roo.form.Action.prototype = {
8689     type : 'default',
8690     failureType : undefined,
8691     response : undefined,
8692     result : undefined,
8693
8694     // interface method
8695     run : function(options){
8696
8697     },
8698
8699     // interface method
8700     success : function(response){
8701
8702     },
8703
8704     // interface method
8705     handleResponse : function(response){
8706
8707     },
8708
8709     // default connection failure
8710     failure : function(response){
8711         
8712         this.response = response;
8713         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8714         this.form.afterAction(this, false);
8715     },
8716
8717     processResponse : function(response){
8718         this.response = response;
8719         if(!response.responseText){
8720             return true;
8721         }
8722         this.result = this.handleResponse(response);
8723         return this.result;
8724     },
8725
8726     // utility functions used internally
8727     getUrl : function(appendParams){
8728         var url = this.options.url || this.form.url || this.form.el.dom.action;
8729         if(appendParams){
8730             var p = this.getParams();
8731             if(p){
8732                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8733             }
8734         }
8735         return url;
8736     },
8737
8738     getMethod : function(){
8739         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8740     },
8741
8742     getParams : function(){
8743         var bp = this.form.baseParams;
8744         var p = this.options.params;
8745         if(p){
8746             if(typeof p == "object"){
8747                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8748             }else if(typeof p == 'string' && bp){
8749                 p += '&' + Roo.urlEncode(bp);
8750             }
8751         }else if(bp){
8752             p = Roo.urlEncode(bp);
8753         }
8754         return p;
8755     },
8756
8757     createCallback : function(){
8758         return {
8759             success: this.success,
8760             failure: this.failure,
8761             scope: this,
8762             timeout: (this.form.timeout*1000),
8763             upload: this.form.fileUpload ? this.success : undefined
8764         };
8765     }
8766 };
8767
8768 Roo.form.Action.Submit = function(form, options){
8769     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8770 };
8771
8772 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8773     type : 'submit',
8774
8775     haveProgress : false,
8776     uploadComplete : false,
8777     
8778     // uploadProgress indicator.
8779     uploadProgress : function()
8780     {
8781         if (!this.form.progressUrl) {
8782             return;
8783         }
8784         
8785         if (!this.haveProgress) {
8786             Roo.MessageBox.progress("Uploading", "Uploading");
8787         }
8788         if (this.uploadComplete) {
8789            Roo.MessageBox.hide();
8790            return;
8791         }
8792         
8793         this.haveProgress = true;
8794    
8795         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8796         
8797         var c = new Roo.data.Connection();
8798         c.request({
8799             url : this.form.progressUrl,
8800             params: {
8801                 id : uid
8802             },
8803             method: 'GET',
8804             success : function(req){
8805                //console.log(data);
8806                 var rdata = false;
8807                 var edata;
8808                 try  {
8809                    rdata = Roo.decode(req.responseText)
8810                 } catch (e) {
8811                     Roo.log("Invalid data from server..");
8812                     Roo.log(edata);
8813                     return;
8814                 }
8815                 if (!rdata || !rdata.success) {
8816                     Roo.log(rdata);
8817                     Roo.MessageBox.alert(Roo.encode(rdata));
8818                     return;
8819                 }
8820                 var data = rdata.data;
8821                 
8822                 if (this.uploadComplete) {
8823                    Roo.MessageBox.hide();
8824                    return;
8825                 }
8826                    
8827                 if (data){
8828                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8829                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8830                     );
8831                 }
8832                 this.uploadProgress.defer(2000,this);
8833             },
8834        
8835             failure: function(data) {
8836                 Roo.log('progress url failed ');
8837                 Roo.log(data);
8838             },
8839             scope : this
8840         });
8841            
8842     },
8843     
8844     
8845     run : function()
8846     {
8847         // run get Values on the form, so it syncs any secondary forms.
8848         this.form.getValues();
8849         
8850         var o = this.options;
8851         var method = this.getMethod();
8852         var isPost = method == 'POST';
8853         if(o.clientValidation === false || this.form.isValid()){
8854             
8855             if (this.form.progressUrl) {
8856                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8857                     (new Date() * 1) + '' + Math.random());
8858                     
8859             } 
8860             
8861             
8862             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8863                 form:this.form.el.dom,
8864                 url:this.getUrl(!isPost),
8865                 method: method,
8866                 params:isPost ? this.getParams() : null,
8867                 isUpload: this.form.fileUpload,
8868                 formData : this.form.formData
8869             }));
8870             
8871             this.uploadProgress();
8872
8873         }else if (o.clientValidation !== false){ // client validation failed
8874             this.failureType = Roo.form.Action.CLIENT_INVALID;
8875             this.form.afterAction(this, false);
8876         }
8877     },
8878
8879     success : function(response)
8880     {
8881         this.uploadComplete= true;
8882         if (this.haveProgress) {
8883             Roo.MessageBox.hide();
8884         }
8885         
8886         
8887         var result = this.processResponse(response);
8888         if(result === true || result.success){
8889             this.form.afterAction(this, true);
8890             return;
8891         }
8892         if(result.errors){
8893             this.form.markInvalid(result.errors);
8894             this.failureType = Roo.form.Action.SERVER_INVALID;
8895         }
8896         this.form.afterAction(this, false);
8897     },
8898     failure : function(response)
8899     {
8900         this.uploadComplete= true;
8901         if (this.haveProgress) {
8902             Roo.MessageBox.hide();
8903         }
8904         
8905         this.response = response;
8906         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8907         this.form.afterAction(this, false);
8908     },
8909     
8910     handleResponse : function(response){
8911         if(this.form.errorReader){
8912             var rs = this.form.errorReader.read(response);
8913             var errors = [];
8914             if(rs.records){
8915                 for(var i = 0, len = rs.records.length; i < len; i++) {
8916                     var r = rs.records[i];
8917                     errors[i] = r.data;
8918                 }
8919             }
8920             if(errors.length < 1){
8921                 errors = null;
8922             }
8923             return {
8924                 success : rs.success,
8925                 errors : errors
8926             };
8927         }
8928         var ret = false;
8929         try {
8930             ret = Roo.decode(response.responseText);
8931         } catch (e) {
8932             ret = {
8933                 success: false,
8934                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8935                 errors : []
8936             };
8937         }
8938         return ret;
8939         
8940     }
8941 });
8942
8943
8944 Roo.form.Action.Load = function(form, options){
8945     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8946     this.reader = this.form.reader;
8947 };
8948
8949 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8950     type : 'load',
8951
8952     run : function(){
8953         
8954         Roo.Ajax.request(Roo.apply(
8955                 this.createCallback(), {
8956                     method:this.getMethod(),
8957                     url:this.getUrl(false),
8958                     params:this.getParams()
8959         }));
8960     },
8961
8962     success : function(response){
8963         
8964         var result = this.processResponse(response);
8965         if(result === true || !result.success || !result.data){
8966             this.failureType = Roo.form.Action.LOAD_FAILURE;
8967             this.form.afterAction(this, false);
8968             return;
8969         }
8970         this.form.clearInvalid();
8971         this.form.setValues(result.data);
8972         this.form.afterAction(this, true);
8973     },
8974
8975     handleResponse : function(response){
8976         if(this.form.reader){
8977             var rs = this.form.reader.read(response);
8978             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8979             return {
8980                 success : rs.success,
8981                 data : data
8982             };
8983         }
8984         return Roo.decode(response.responseText);
8985     }
8986 });
8987
8988 Roo.form.Action.ACTION_TYPES = {
8989     'load' : Roo.form.Action.Load,
8990     'submit' : Roo.form.Action.Submit
8991 };/*
8992  * - LGPL
8993  *
8994  * form
8995  *
8996  */
8997
8998 /**
8999  * @class Roo.bootstrap.Form
9000  * @extends Roo.bootstrap.Component
9001  * Bootstrap Form class
9002  * @cfg {String} method  GET | POST (default POST)
9003  * @cfg {String} labelAlign top | left (default top)
9004  * @cfg {String} align left  | right - for navbars
9005  * @cfg {Boolean} loadMask load mask when submit (default true)
9006
9007  *
9008  * @constructor
9009  * Create a new Form
9010  * @param {Object} config The config object
9011  */
9012
9013
9014 Roo.bootstrap.Form = function(config){
9015     
9016     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9017     
9018     Roo.bootstrap.Form.popover.apply();
9019     
9020     this.addEvents({
9021         /**
9022          * @event clientvalidation
9023          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9024          * @param {Form} this
9025          * @param {Boolean} valid true if the form has passed client-side validation
9026          */
9027         clientvalidation: true,
9028         /**
9029          * @event beforeaction
9030          * Fires before any action is performed. Return false to cancel the action.
9031          * @param {Form} this
9032          * @param {Action} action The action to be performed
9033          */
9034         beforeaction: true,
9035         /**
9036          * @event actionfailed
9037          * Fires when an action fails.
9038          * @param {Form} this
9039          * @param {Action} action The action that failed
9040          */
9041         actionfailed : true,
9042         /**
9043          * @event actioncomplete
9044          * Fires when an action is completed.
9045          * @param {Form} this
9046          * @param {Action} action The action that completed
9047          */
9048         actioncomplete : true
9049     });
9050 };
9051
9052 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9053
9054      /**
9055      * @cfg {String} method
9056      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9057      */
9058     method : 'POST',
9059     /**
9060      * @cfg {String} url
9061      * The URL to use for form actions if one isn't supplied in the action options.
9062      */
9063     /**
9064      * @cfg {Boolean} fileUpload
9065      * Set to true if this form is a file upload.
9066      */
9067
9068     /**
9069      * @cfg {Object} baseParams
9070      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9071      */
9072
9073     /**
9074      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9075      */
9076     timeout: 30,
9077     /**
9078      * @cfg {Sting} align (left|right) for navbar forms
9079      */
9080     align : 'left',
9081
9082     // private
9083     activeAction : null,
9084
9085     /**
9086      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9087      * element by passing it or its id or mask the form itself by passing in true.
9088      * @type Mixed
9089      */
9090     waitMsgTarget : false,
9091
9092     loadMask : true,
9093     
9094     /**
9095      * @cfg {Boolean} errorMask (true|false) default false
9096      */
9097     errorMask : false,
9098     
9099     /**
9100      * @cfg {Number} maskOffset Default 100
9101      */
9102     maskOffset : 100,
9103     
9104     /**
9105      * @cfg {Boolean} maskBody
9106      */
9107     maskBody : false,
9108
9109     getAutoCreate : function(){
9110
9111         var cfg = {
9112             tag: 'form',
9113             method : this.method || 'POST',
9114             id : this.id || Roo.id(),
9115             cls : ''
9116         };
9117         if (this.parent().xtype.match(/^Nav/)) {
9118             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9119
9120         }
9121
9122         if (this.labelAlign == 'left' ) {
9123             cfg.cls += ' form-horizontal';
9124         }
9125
9126
9127         return cfg;
9128     },
9129     initEvents : function()
9130     {
9131         this.el.on('submit', this.onSubmit, this);
9132         // this was added as random key presses on the form where triggering form submit.
9133         this.el.on('keypress', function(e) {
9134             if (e.getCharCode() != 13) {
9135                 return true;
9136             }
9137             // we might need to allow it for textareas.. and some other items.
9138             // check e.getTarget().
9139
9140             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9141                 return true;
9142             }
9143
9144             Roo.log("keypress blocked");
9145
9146             e.preventDefault();
9147             return false;
9148         });
9149         
9150     },
9151     // private
9152     onSubmit : function(e){
9153         e.stopEvent();
9154     },
9155
9156      /**
9157      * Returns true if client-side validation on the form is successful.
9158      * @return Boolean
9159      */
9160     isValid : function(){
9161         var items = this.getItems();
9162         var valid = true;
9163         var target = false;
9164         
9165         items.each(function(f){
9166             
9167             if(f.validate()){
9168                 return;
9169             }
9170             
9171             Roo.log('invalid field: ' + f.name);
9172             
9173             valid = false;
9174
9175             if(!target && f.el.isVisible(true)){
9176                 target = f;
9177             }
9178            
9179         });
9180         
9181         if(this.errorMask && !valid){
9182             Roo.bootstrap.Form.popover.mask(this, target);
9183         }
9184         
9185         return valid;
9186     },
9187     
9188     /**
9189      * Returns true if any fields in this form have changed since their original load.
9190      * @return Boolean
9191      */
9192     isDirty : function(){
9193         var dirty = false;
9194         var items = this.getItems();
9195         items.each(function(f){
9196            if(f.isDirty()){
9197                dirty = true;
9198                return false;
9199            }
9200            return true;
9201         });
9202         return dirty;
9203     },
9204      /**
9205      * Performs a predefined action (submit or load) or custom actions you define on this form.
9206      * @param {String} actionName The name of the action type
9207      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9208      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9209      * accept other config options):
9210      * <pre>
9211 Property          Type             Description
9212 ----------------  ---------------  ----------------------------------------------------------------------------------
9213 url               String           The url for the action (defaults to the form's url)
9214 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9215 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9216 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9217                                    validate the form on the client (defaults to false)
9218      * </pre>
9219      * @return {BasicForm} this
9220      */
9221     doAction : function(action, options){
9222         if(typeof action == 'string'){
9223             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9224         }
9225         if(this.fireEvent('beforeaction', this, action) !== false){
9226             this.beforeAction(action);
9227             action.run.defer(100, action);
9228         }
9229         return this;
9230     },
9231
9232     // private
9233     beforeAction : function(action){
9234         var o = action.options;
9235         
9236         if(this.loadMask){
9237             
9238             if(this.maskBody){
9239                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9240             } else {
9241                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9242             }
9243         }
9244         // not really supported yet.. ??
9245
9246         //if(this.waitMsgTarget === true){
9247         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9248         //}else if(this.waitMsgTarget){
9249         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9250         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9251         //}else {
9252         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9253        // }
9254
9255     },
9256
9257     // private
9258     afterAction : function(action, success){
9259         this.activeAction = null;
9260         var o = action.options;
9261
9262         if(this.loadMask){
9263             
9264             if(this.maskBody){
9265                 Roo.get(document.body).unmask();
9266             } else {
9267                 this.el.unmask();
9268             }
9269         }
9270         
9271         //if(this.waitMsgTarget === true){
9272 //            this.el.unmask();
9273         //}else if(this.waitMsgTarget){
9274         //    this.waitMsgTarget.unmask();
9275         //}else{
9276         //    Roo.MessageBox.updateProgress(1);
9277         //    Roo.MessageBox.hide();
9278        // }
9279         //
9280         if(success){
9281             if(o.reset){
9282                 this.reset();
9283             }
9284             Roo.callback(o.success, o.scope, [this, action]);
9285             this.fireEvent('actioncomplete', this, action);
9286
9287         }else{
9288
9289             // failure condition..
9290             // we have a scenario where updates need confirming.
9291             // eg. if a locking scenario exists..
9292             // we look for { errors : { needs_confirm : true }} in the response.
9293             if (
9294                 (typeof(action.result) != 'undefined')  &&
9295                 (typeof(action.result.errors) != 'undefined')  &&
9296                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9297            ){
9298                 var _t = this;
9299                 Roo.log("not supported yet");
9300                  /*
9301
9302                 Roo.MessageBox.confirm(
9303                     "Change requires confirmation",
9304                     action.result.errorMsg,
9305                     function(r) {
9306                         if (r != 'yes') {
9307                             return;
9308                         }
9309                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9310                     }
9311
9312                 );
9313                 */
9314
9315
9316                 return;
9317             }
9318
9319             Roo.callback(o.failure, o.scope, [this, action]);
9320             // show an error message if no failed handler is set..
9321             if (!this.hasListener('actionfailed')) {
9322                 Roo.log("need to add dialog support");
9323                 /*
9324                 Roo.MessageBox.alert("Error",
9325                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9326                         action.result.errorMsg :
9327                         "Saving Failed, please check your entries or try again"
9328                 );
9329                 */
9330             }
9331
9332             this.fireEvent('actionfailed', this, action);
9333         }
9334
9335     },
9336     /**
9337      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9338      * @param {String} id The value to search for
9339      * @return Field
9340      */
9341     findField : function(id){
9342         var items = this.getItems();
9343         var field = items.get(id);
9344         if(!field){
9345              items.each(function(f){
9346                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9347                     field = f;
9348                     return false;
9349                 }
9350                 return true;
9351             });
9352         }
9353         return field || null;
9354     },
9355      /**
9356      * Mark fields in this form invalid in bulk.
9357      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9358      * @return {BasicForm} this
9359      */
9360     markInvalid : function(errors){
9361         if(errors instanceof Array){
9362             for(var i = 0, len = errors.length; i < len; i++){
9363                 var fieldError = errors[i];
9364                 var f = this.findField(fieldError.id);
9365                 if(f){
9366                     f.markInvalid(fieldError.msg);
9367                 }
9368             }
9369         }else{
9370             var field, id;
9371             for(id in errors){
9372                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9373                     field.markInvalid(errors[id]);
9374                 }
9375             }
9376         }
9377         //Roo.each(this.childForms || [], function (f) {
9378         //    f.markInvalid(errors);
9379         //});
9380
9381         return this;
9382     },
9383
9384     /**
9385      * Set values for fields in this form in bulk.
9386      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9387      * @return {BasicForm} this
9388      */
9389     setValues : function(values){
9390         if(values instanceof Array){ // array of objects
9391             for(var i = 0, len = values.length; i < len; i++){
9392                 var v = values[i];
9393                 var f = this.findField(v.id);
9394                 if(f){
9395                     f.setValue(v.value);
9396                     if(this.trackResetOnLoad){
9397                         f.originalValue = f.getValue();
9398                     }
9399                 }
9400             }
9401         }else{ // object hash
9402             var field, id;
9403             for(id in values){
9404                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9405
9406                     if (field.setFromData &&
9407                         field.valueField &&
9408                         field.displayField &&
9409                         // combos' with local stores can
9410                         // be queried via setValue()
9411                         // to set their value..
9412                         (field.store && !field.store.isLocal)
9413                         ) {
9414                         // it's a combo
9415                         var sd = { };
9416                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9417                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9418                         field.setFromData(sd);
9419
9420                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9421                         
9422                         field.setFromData(values);
9423                         
9424                     } else {
9425                         field.setValue(values[id]);
9426                     }
9427
9428
9429                     if(this.trackResetOnLoad){
9430                         field.originalValue = field.getValue();
9431                     }
9432                 }
9433             }
9434         }
9435
9436         //Roo.each(this.childForms || [], function (f) {
9437         //    f.setValues(values);
9438         //});
9439
9440         return this;
9441     },
9442
9443     /**
9444      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9445      * they are returned as an array.
9446      * @param {Boolean} asString
9447      * @return {Object}
9448      */
9449     getValues : function(asString){
9450         //if (this.childForms) {
9451             // copy values from the child forms
9452         //    Roo.each(this.childForms, function (f) {
9453         //        this.setValues(f.getValues());
9454         //    }, this);
9455         //}
9456
9457
9458
9459         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9460         if(asString === true){
9461             return fs;
9462         }
9463         return Roo.urlDecode(fs);
9464     },
9465
9466     /**
9467      * Returns the fields in this form as an object with key/value pairs.
9468      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9469      * @return {Object}
9470      */
9471     getFieldValues : function(with_hidden)
9472     {
9473         var items = this.getItems();
9474         var ret = {};
9475         items.each(function(f){
9476             
9477             if (!f.getName()) {
9478                 return;
9479             }
9480             
9481             var v = f.getValue();
9482             
9483             if (f.inputType =='radio') {
9484                 if (typeof(ret[f.getName()]) == 'undefined') {
9485                     ret[f.getName()] = ''; // empty..
9486                 }
9487
9488                 if (!f.el.dom.checked) {
9489                     return;
9490
9491                 }
9492                 v = f.el.dom.value;
9493
9494             }
9495             
9496             if(f.xtype == 'MoneyField'){
9497                 ret[f.currencyName] = f.getCurrency();
9498             }
9499
9500             // not sure if this supported any more..
9501             if ((typeof(v) == 'object') && f.getRawValue) {
9502                 v = f.getRawValue() ; // dates..
9503             }
9504             // combo boxes where name != hiddenName...
9505             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9506                 ret[f.name] = f.getRawValue();
9507             }
9508             ret[f.getName()] = v;
9509         });
9510
9511         return ret;
9512     },
9513
9514     /**
9515      * Clears all invalid messages in this form.
9516      * @return {BasicForm} this
9517      */
9518     clearInvalid : function(){
9519         var items = this.getItems();
9520
9521         items.each(function(f){
9522            f.clearInvalid();
9523         });
9524
9525         return this;
9526     },
9527
9528     /**
9529      * Resets this form.
9530      * @return {BasicForm} this
9531      */
9532     reset : function(){
9533         var items = this.getItems();
9534         items.each(function(f){
9535             f.reset();
9536         });
9537
9538         Roo.each(this.childForms || [], function (f) {
9539             f.reset();
9540         });
9541
9542
9543         return this;
9544     },
9545     
9546     getItems : function()
9547     {
9548         var r=new Roo.util.MixedCollection(false, function(o){
9549             return o.id || (o.id = Roo.id());
9550         });
9551         var iter = function(el) {
9552             if (el.inputEl) {
9553                 r.add(el);
9554             }
9555             if (!el.items) {
9556                 return;
9557             }
9558             Roo.each(el.items,function(e) {
9559                 iter(e);
9560             });
9561         };
9562
9563         iter(this);
9564         return r;
9565     },
9566     
9567     hideFields : function(items)
9568     {
9569         Roo.each(items, function(i){
9570             
9571             var f = this.findField(i);
9572             
9573             if(!f){
9574                 return;
9575             }
9576             
9577             f.hide();
9578             
9579         }, this);
9580     },
9581     
9582     showFields : function(items)
9583     {
9584         Roo.each(items, function(i){
9585             
9586             var f = this.findField(i);
9587             
9588             if(!f){
9589                 return;
9590             }
9591             
9592             f.show();
9593             
9594         }, this);
9595     }
9596
9597 });
9598
9599 Roo.apply(Roo.bootstrap.Form, {
9600     
9601     popover : {
9602         
9603         padding : 5,
9604         
9605         isApplied : false,
9606         
9607         isMasked : false,
9608         
9609         form : false,
9610         
9611         target : false,
9612         
9613         toolTip : false,
9614         
9615         intervalID : false,
9616         
9617         maskEl : false,
9618         
9619         apply : function()
9620         {
9621             if(this.isApplied){
9622                 return;
9623             }
9624             
9625             this.maskEl = {
9626                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9627                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9628                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9629                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9630             };
9631             
9632             this.maskEl.top.enableDisplayMode("block");
9633             this.maskEl.left.enableDisplayMode("block");
9634             this.maskEl.bottom.enableDisplayMode("block");
9635             this.maskEl.right.enableDisplayMode("block");
9636             
9637             this.toolTip = new Roo.bootstrap.Tooltip({
9638                 cls : 'roo-form-error-popover',
9639                 alignment : {
9640                     'left' : ['r-l', [-2,0], 'right'],
9641                     'right' : ['l-r', [2,0], 'left'],
9642                     'bottom' : ['tl-bl', [0,2], 'top'],
9643                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9644                 }
9645             });
9646             
9647             this.toolTip.render(Roo.get(document.body));
9648
9649             this.toolTip.el.enableDisplayMode("block");
9650             
9651             Roo.get(document.body).on('click', function(){
9652                 this.unmask();
9653             }, this);
9654             
9655             Roo.get(document.body).on('touchstart', function(){
9656                 this.unmask();
9657             }, this);
9658             
9659             this.isApplied = true
9660         },
9661         
9662         mask : function(form, target)
9663         {
9664             this.form = form;
9665             
9666             this.target = target;
9667             
9668             if(!this.form.errorMask || !target.el){
9669                 return;
9670             }
9671             
9672             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9673             
9674             Roo.log(scrollable);
9675             
9676             var ot = this.target.el.calcOffsetsTo(scrollable);
9677             
9678             var scrollTo = ot[1] - this.form.maskOffset;
9679             
9680             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9681             
9682             scrollable.scrollTo('top', scrollTo);
9683             
9684             var box = this.target.el.getBox();
9685             Roo.log(box);
9686             var zIndex = Roo.bootstrap.Modal.zIndex++;
9687
9688             
9689             this.maskEl.top.setStyle('position', 'absolute');
9690             this.maskEl.top.setStyle('z-index', zIndex);
9691             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9692             this.maskEl.top.setLeft(0);
9693             this.maskEl.top.setTop(0);
9694             this.maskEl.top.show();
9695             
9696             this.maskEl.left.setStyle('position', 'absolute');
9697             this.maskEl.left.setStyle('z-index', zIndex);
9698             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9699             this.maskEl.left.setLeft(0);
9700             this.maskEl.left.setTop(box.y - this.padding);
9701             this.maskEl.left.show();
9702
9703             this.maskEl.bottom.setStyle('position', 'absolute');
9704             this.maskEl.bottom.setStyle('z-index', zIndex);
9705             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9706             this.maskEl.bottom.setLeft(0);
9707             this.maskEl.bottom.setTop(box.bottom + this.padding);
9708             this.maskEl.bottom.show();
9709
9710             this.maskEl.right.setStyle('position', 'absolute');
9711             this.maskEl.right.setStyle('z-index', zIndex);
9712             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9713             this.maskEl.right.setLeft(box.right + this.padding);
9714             this.maskEl.right.setTop(box.y - this.padding);
9715             this.maskEl.right.show();
9716
9717             this.toolTip.bindEl = this.target.el;
9718
9719             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9720
9721             var tip = this.target.blankText;
9722
9723             if(this.target.getValue() !== '' ) {
9724                 
9725                 if (this.target.invalidText.length) {
9726                     tip = this.target.invalidText;
9727                 } else if (this.target.regexText.length){
9728                     tip = this.target.regexText;
9729                 }
9730             }
9731
9732             this.toolTip.show(tip);
9733
9734             this.intervalID = window.setInterval(function() {
9735                 Roo.bootstrap.Form.popover.unmask();
9736             }, 10000);
9737
9738             window.onwheel = function(){ return false;};
9739             
9740             (function(){ this.isMasked = true; }).defer(500, this);
9741             
9742         },
9743         
9744         unmask : function()
9745         {
9746             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9747                 return;
9748             }
9749             
9750             this.maskEl.top.setStyle('position', 'absolute');
9751             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9752             this.maskEl.top.hide();
9753
9754             this.maskEl.left.setStyle('position', 'absolute');
9755             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9756             this.maskEl.left.hide();
9757
9758             this.maskEl.bottom.setStyle('position', 'absolute');
9759             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9760             this.maskEl.bottom.hide();
9761
9762             this.maskEl.right.setStyle('position', 'absolute');
9763             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9764             this.maskEl.right.hide();
9765             
9766             this.toolTip.hide();
9767             
9768             this.toolTip.el.hide();
9769             
9770             window.onwheel = function(){ return true;};
9771             
9772             if(this.intervalID){
9773                 window.clearInterval(this.intervalID);
9774                 this.intervalID = false;
9775             }
9776             
9777             this.isMasked = false;
9778             
9779         }
9780         
9781     }
9782     
9783 });
9784
9785 /*
9786  * Based on:
9787  * Ext JS Library 1.1.1
9788  * Copyright(c) 2006-2007, Ext JS, LLC.
9789  *
9790  * Originally Released Under LGPL - original licence link has changed is not relivant.
9791  *
9792  * Fork - LGPL
9793  * <script type="text/javascript">
9794  */
9795 /**
9796  * @class Roo.form.VTypes
9797  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9798  * @singleton
9799  */
9800 Roo.form.VTypes = function(){
9801     // closure these in so they are only created once.
9802     var alpha = /^[a-zA-Z_]+$/;
9803     var alphanum = /^[a-zA-Z0-9_]+$/;
9804     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9805     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9806
9807     // All these messages and functions are configurable
9808     return {
9809         /**
9810          * The function used to validate email addresses
9811          * @param {String} value The email address
9812          */
9813         'email' : function(v){
9814             return email.test(v);
9815         },
9816         /**
9817          * The error text to display when the email validation function returns false
9818          * @type String
9819          */
9820         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9821         /**
9822          * The keystroke filter mask to be applied on email input
9823          * @type RegExp
9824          */
9825         'emailMask' : /[a-z0-9_\.\-@]/i,
9826
9827         /**
9828          * The function used to validate URLs
9829          * @param {String} value The URL
9830          */
9831         'url' : function(v){
9832             return url.test(v);
9833         },
9834         /**
9835          * The error text to display when the url validation function returns false
9836          * @type String
9837          */
9838         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9839         
9840         /**
9841          * The function used to validate alpha values
9842          * @param {String} value The value
9843          */
9844         'alpha' : function(v){
9845             return alpha.test(v);
9846         },
9847         /**
9848          * The error text to display when the alpha validation function returns false
9849          * @type String
9850          */
9851         'alphaText' : 'This field should only contain letters and _',
9852         /**
9853          * The keystroke filter mask to be applied on alpha input
9854          * @type RegExp
9855          */
9856         'alphaMask' : /[a-z_]/i,
9857
9858         /**
9859          * The function used to validate alphanumeric values
9860          * @param {String} value The value
9861          */
9862         'alphanum' : function(v){
9863             return alphanum.test(v);
9864         },
9865         /**
9866          * The error text to display when the alphanumeric validation function returns false
9867          * @type String
9868          */
9869         'alphanumText' : 'This field should only contain letters, numbers and _',
9870         /**
9871          * The keystroke filter mask to be applied on alphanumeric input
9872          * @type RegExp
9873          */
9874         'alphanumMask' : /[a-z0-9_]/i
9875     };
9876 }();/*
9877  * - LGPL
9878  *
9879  * Input
9880  * 
9881  */
9882
9883 /**
9884  * @class Roo.bootstrap.Input
9885  * @extends Roo.bootstrap.Component
9886  * Bootstrap Input class
9887  * @cfg {Boolean} disabled is it disabled
9888  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9889  * @cfg {String} name name of the input
9890  * @cfg {string} fieldLabel - the label associated
9891  * @cfg {string} placeholder - placeholder to put in text.
9892  * @cfg {string}  before - input group add on before
9893  * @cfg {string} after - input group add on after
9894  * @cfg {string} size - (lg|sm) or leave empty..
9895  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9896  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9897  * @cfg {Number} md colspan out of 12 for computer-sized screens
9898  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9899  * @cfg {string} value default value of the input
9900  * @cfg {Number} labelWidth set the width of label 
9901  * @cfg {Number} labellg set the width of label (1-12)
9902  * @cfg {Number} labelmd set the width of label (1-12)
9903  * @cfg {Number} labelsm set the width of label (1-12)
9904  * @cfg {Number} labelxs set the width of label (1-12)
9905  * @cfg {String} labelAlign (top|left)
9906  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9907  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9908  * @cfg {String} indicatorpos (left|right) default left
9909  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9910  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9911
9912  * @cfg {String} align (left|center|right) Default left
9913  * @cfg {Boolean} forceFeedback (true|false) Default false
9914  * 
9915  * @constructor
9916  * Create a new Input
9917  * @param {Object} config The config object
9918  */
9919
9920 Roo.bootstrap.Input = function(config){
9921     
9922     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9923     
9924     this.addEvents({
9925         /**
9926          * @event focus
9927          * Fires when this field receives input focus.
9928          * @param {Roo.form.Field} this
9929          */
9930         focus : true,
9931         /**
9932          * @event blur
9933          * Fires when this field loses input focus.
9934          * @param {Roo.form.Field} this
9935          */
9936         blur : true,
9937         /**
9938          * @event specialkey
9939          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9940          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9941          * @param {Roo.form.Field} this
9942          * @param {Roo.EventObject} e The event object
9943          */
9944         specialkey : true,
9945         /**
9946          * @event change
9947          * Fires just before the field blurs if the field value has changed.
9948          * @param {Roo.form.Field} this
9949          * @param {Mixed} newValue The new value
9950          * @param {Mixed} oldValue The original value
9951          */
9952         change : true,
9953         /**
9954          * @event invalid
9955          * Fires after the field has been marked as invalid.
9956          * @param {Roo.form.Field} this
9957          * @param {String} msg The validation message
9958          */
9959         invalid : true,
9960         /**
9961          * @event valid
9962          * Fires after the field has been validated with no errors.
9963          * @param {Roo.form.Field} this
9964          */
9965         valid : true,
9966          /**
9967          * @event keyup
9968          * Fires after the key up
9969          * @param {Roo.form.Field} this
9970          * @param {Roo.EventObject}  e The event Object
9971          */
9972         keyup : true
9973     });
9974 };
9975
9976 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9977      /**
9978      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9979       automatic validation (defaults to "keyup").
9980      */
9981     validationEvent : "keyup",
9982      /**
9983      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9984      */
9985     validateOnBlur : true,
9986     /**
9987      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9988      */
9989     validationDelay : 250,
9990      /**
9991      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9992      */
9993     focusClass : "x-form-focus",  // not needed???
9994     
9995        
9996     /**
9997      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9998      */
9999     invalidClass : "has-warning",
10000     
10001     /**
10002      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10003      */
10004     validClass : "has-success",
10005     
10006     /**
10007      * @cfg {Boolean} hasFeedback (true|false) default true
10008      */
10009     hasFeedback : true,
10010     
10011     /**
10012      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10013      */
10014     invalidFeedbackClass : "glyphicon-warning-sign",
10015     
10016     /**
10017      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10018      */
10019     validFeedbackClass : "glyphicon-ok",
10020     
10021     /**
10022      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10023      */
10024     selectOnFocus : false,
10025     
10026      /**
10027      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10028      */
10029     maskRe : null,
10030        /**
10031      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10032      */
10033     vtype : null,
10034     
10035       /**
10036      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10037      */
10038     disableKeyFilter : false,
10039     
10040        /**
10041      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10042      */
10043     disabled : false,
10044      /**
10045      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10046      */
10047     allowBlank : true,
10048     /**
10049      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10050      */
10051     blankText : "Please complete this mandatory field",
10052     
10053      /**
10054      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10055      */
10056     minLength : 0,
10057     /**
10058      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10059      */
10060     maxLength : Number.MAX_VALUE,
10061     /**
10062      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10063      */
10064     minLengthText : "The minimum length for this field is {0}",
10065     /**
10066      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10067      */
10068     maxLengthText : "The maximum length for this field is {0}",
10069   
10070     
10071     /**
10072      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10073      * If available, this function will be called only after the basic validators all return true, and will be passed the
10074      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10075      */
10076     validator : null,
10077     /**
10078      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10079      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10080      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10081      */
10082     regex : null,
10083     /**
10084      * @cfg {String} regexText -- Depricated - use Invalid Text
10085      */
10086     regexText : "",
10087     
10088     /**
10089      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10090      */
10091     invalidText : "",
10092     
10093     
10094     
10095     autocomplete: false,
10096     
10097     
10098     fieldLabel : '',
10099     inputType : 'text',
10100     
10101     name : false,
10102     placeholder: false,
10103     before : false,
10104     after : false,
10105     size : false,
10106     hasFocus : false,
10107     preventMark: false,
10108     isFormField : true,
10109     value : '',
10110     labelWidth : 2,
10111     labelAlign : false,
10112     readOnly : false,
10113     align : false,
10114     formatedValue : false,
10115     forceFeedback : false,
10116     
10117     indicatorpos : 'left',
10118     
10119     labellg : 0,
10120     labelmd : 0,
10121     labelsm : 0,
10122     labelxs : 0,
10123     
10124     capture : '',
10125     accept : '',
10126     
10127     parentLabelAlign : function()
10128     {
10129         var parent = this;
10130         while (parent.parent()) {
10131             parent = parent.parent();
10132             if (typeof(parent.labelAlign) !='undefined') {
10133                 return parent.labelAlign;
10134             }
10135         }
10136         return 'left';
10137         
10138     },
10139     
10140     getAutoCreate : function()
10141     {
10142         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10143         
10144         var id = Roo.id();
10145         
10146         var cfg = {};
10147         
10148         if(this.inputType != 'hidden'){
10149             cfg.cls = 'form-group' //input-group
10150         }
10151         
10152         var input =  {
10153             tag: 'input',
10154             id : id,
10155             type : this.inputType,
10156             value : this.value,
10157             cls : 'form-control',
10158             placeholder : this.placeholder || '',
10159             autocomplete : this.autocomplete || 'new-password'
10160         };
10161         
10162         if(this.capture.length){
10163             input.capture = this.capture;
10164         }
10165         
10166         if(this.accept.length){
10167             input.accept = this.accept + "/*";
10168         }
10169         
10170         if(this.align){
10171             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10172         }
10173         
10174         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10175             input.maxLength = this.maxLength;
10176         }
10177         
10178         if (this.disabled) {
10179             input.disabled=true;
10180         }
10181         
10182         if (this.readOnly) {
10183             input.readonly=true;
10184         }
10185         
10186         if (this.name) {
10187             input.name = this.name;
10188         }
10189         
10190         if (this.size) {
10191             input.cls += ' input-' + this.size;
10192         }
10193         
10194         var settings=this;
10195         ['xs','sm','md','lg'].map(function(size){
10196             if (settings[size]) {
10197                 cfg.cls += ' col-' + size + '-' + settings[size];
10198             }
10199         });
10200         
10201         var inputblock = input;
10202         
10203         var feedback = {
10204             tag: 'span',
10205             cls: 'glyphicon form-control-feedback'
10206         };
10207             
10208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10209             
10210             inputblock = {
10211                 cls : 'has-feedback',
10212                 cn :  [
10213                     input,
10214                     feedback
10215                 ] 
10216             };  
10217         }
10218         
10219         if (this.before || this.after) {
10220             
10221             inputblock = {
10222                 cls : 'input-group',
10223                 cn :  [] 
10224             };
10225             
10226             if (this.before && typeof(this.before) == 'string') {
10227                 
10228                 inputblock.cn.push({
10229                     tag :'span',
10230                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10231                     html : this.before
10232                 });
10233             }
10234             if (this.before && typeof(this.before) == 'object') {
10235                 this.before = Roo.factory(this.before);
10236                 
10237                 inputblock.cn.push({
10238                     tag :'span',
10239                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10240                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10241                 });
10242             }
10243             
10244             inputblock.cn.push(input);
10245             
10246             if (this.after && typeof(this.after) == 'string') {
10247                 inputblock.cn.push({
10248                     tag :'span',
10249                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10250                     html : this.after
10251                 });
10252             }
10253             if (this.after && typeof(this.after) == 'object') {
10254                 this.after = Roo.factory(this.after);
10255                 
10256                 inputblock.cn.push({
10257                     tag :'span',
10258                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10259                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10260                 });
10261             }
10262             
10263             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10264                 inputblock.cls += ' has-feedback';
10265                 inputblock.cn.push(feedback);
10266             }
10267         };
10268         var indicator = {
10269             tag : 'i',
10270             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10271             tooltip : 'This field is required'
10272         };
10273         if (Roo.bootstrap.version == 4) {
10274             indicator = {
10275                 tag : 'i',
10276                 style : 'display-none'
10277             };
10278         }
10279         if (align ==='left' && this.fieldLabel.length) {
10280             
10281             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10282             
10283             cfg.cn = [
10284                 indicator,
10285                 {
10286                     tag: 'label',
10287                     'for' :  id,
10288                     cls : 'control-label col-form-label',
10289                     html : this.fieldLabel
10290
10291                 },
10292                 {
10293                     cls : "", 
10294                     cn: [
10295                         inputblock
10296                     ]
10297                 }
10298             ];
10299             
10300             var labelCfg = cfg.cn[1];
10301             var contentCfg = cfg.cn[2];
10302             
10303             if(this.indicatorpos == 'right'){
10304                 cfg.cn = [
10305                     {
10306                         tag: 'label',
10307                         'for' :  id,
10308                         cls : 'control-label col-form-label',
10309                         cn : [
10310                             {
10311                                 tag : 'span',
10312                                 html : this.fieldLabel
10313                             },
10314                             indicator
10315                         ]
10316                     },
10317                     {
10318                         cls : "",
10319                         cn: [
10320                             inputblock
10321                         ]
10322                     }
10323
10324                 ];
10325                 
10326                 labelCfg = cfg.cn[0];
10327                 contentCfg = cfg.cn[1];
10328             
10329             }
10330             
10331             if(this.labelWidth > 12){
10332                 labelCfg.style = "width: " + this.labelWidth + 'px';
10333             }
10334             
10335             if(this.labelWidth < 13 && this.labelmd == 0){
10336                 this.labelmd = this.labelWidth;
10337             }
10338             
10339             if(this.labellg > 0){
10340                 labelCfg.cls += ' col-lg-' + this.labellg;
10341                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10342             }
10343             
10344             if(this.labelmd > 0){
10345                 labelCfg.cls += ' col-md-' + this.labelmd;
10346                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10347             }
10348             
10349             if(this.labelsm > 0){
10350                 labelCfg.cls += ' col-sm-' + this.labelsm;
10351                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10352             }
10353             
10354             if(this.labelxs > 0){
10355                 labelCfg.cls += ' col-xs-' + this.labelxs;
10356                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10357             }
10358             
10359             
10360         } else if ( this.fieldLabel.length) {
10361                 
10362             cfg.cn = [
10363                 {
10364                     tag : 'i',
10365                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10366                     tooltip : 'This field is required'
10367                 },
10368                 {
10369                     tag: 'label',
10370                    //cls : 'input-group-addon',
10371                     html : this.fieldLabel
10372
10373                 },
10374
10375                inputblock
10376
10377            ];
10378            
10379            if(this.indicatorpos == 'right'){
10380                 
10381                 cfg.cn = [
10382                     {
10383                         tag: 'label',
10384                        //cls : 'input-group-addon',
10385                         html : this.fieldLabel
10386
10387                     },
10388                     {
10389                         tag : 'i',
10390                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10391                         tooltip : 'This field is required'
10392                     },
10393
10394                    inputblock
10395
10396                ];
10397
10398             }
10399
10400         } else {
10401             
10402             cfg.cn = [
10403
10404                     inputblock
10405
10406             ];
10407                 
10408                 
10409         };
10410         
10411         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10412            cfg.cls += ' navbar-form';
10413         }
10414         
10415         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10416             // on BS4 we do this only if not form 
10417             cfg.cls += ' navbar-form';
10418             cfg.tag = 'li';
10419         }
10420         
10421         return cfg;
10422         
10423     },
10424     /**
10425      * return the real input element.
10426      */
10427     inputEl: function ()
10428     {
10429         return this.el.select('input.form-control',true).first();
10430     },
10431     
10432     tooltipEl : function()
10433     {
10434         return this.inputEl();
10435     },
10436     
10437     indicatorEl : function()
10438     {
10439         if (Roo.bootstrap.version == 4) {
10440             return false; // not enabled in v4 yet.
10441         }
10442         
10443         var indicator = this.el.select('i.roo-required-indicator',true).first();
10444         
10445         if(!indicator){
10446             return false;
10447         }
10448         
10449         return indicator;
10450         
10451     },
10452     
10453     setDisabled : function(v)
10454     {
10455         var i  = this.inputEl().dom;
10456         if (!v) {
10457             i.removeAttribute('disabled');
10458             return;
10459             
10460         }
10461         i.setAttribute('disabled','true');
10462     },
10463     initEvents : function()
10464     {
10465           
10466         this.inputEl().on("keydown" , this.fireKey,  this);
10467         this.inputEl().on("focus", this.onFocus,  this);
10468         this.inputEl().on("blur", this.onBlur,  this);
10469         
10470         this.inputEl().relayEvent('keyup', this);
10471         
10472         this.indicator = this.indicatorEl();
10473         
10474         if(this.indicator){
10475             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10476         }
10477  
10478         // reference to original value for reset
10479         this.originalValue = this.getValue();
10480         //Roo.form.TextField.superclass.initEvents.call(this);
10481         if(this.validationEvent == 'keyup'){
10482             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10483             this.inputEl().on('keyup', this.filterValidation, this);
10484         }
10485         else if(this.validationEvent !== false){
10486             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10487         }
10488         
10489         if(this.selectOnFocus){
10490             this.on("focus", this.preFocus, this);
10491             
10492         }
10493         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10494             this.inputEl().on("keypress", this.filterKeys, this);
10495         } else {
10496             this.inputEl().relayEvent('keypress', this);
10497         }
10498        /* if(this.grow){
10499             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10500             this.el.on("click", this.autoSize,  this);
10501         }
10502         */
10503         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10504             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10505         }
10506         
10507         if (typeof(this.before) == 'object') {
10508             this.before.render(this.el.select('.roo-input-before',true).first());
10509         }
10510         if (typeof(this.after) == 'object') {
10511             this.after.render(this.el.select('.roo-input-after',true).first());
10512         }
10513         
10514         this.inputEl().on('change', this.onChange, this);
10515         
10516     },
10517     filterValidation : function(e){
10518         if(!e.isNavKeyPress()){
10519             this.validationTask.delay(this.validationDelay);
10520         }
10521     },
10522      /**
10523      * Validates the field value
10524      * @return {Boolean} True if the value is valid, else false
10525      */
10526     validate : function(){
10527         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10528         if(this.disabled || this.validateValue(this.getRawValue())){
10529             this.markValid();
10530             return true;
10531         }
10532         
10533         this.markInvalid();
10534         return false;
10535     },
10536     
10537     
10538     /**
10539      * Validates a value according to the field's validation rules and marks the field as invalid
10540      * if the validation fails
10541      * @param {Mixed} value The value to validate
10542      * @return {Boolean} True if the value is valid, else false
10543      */
10544     validateValue : function(value)
10545     {
10546         if(this.getVisibilityEl().hasClass('hidden')){
10547             return true;
10548         }
10549         
10550         if(value.length < 1)  { // if it's blank
10551             if(this.allowBlank){
10552                 return true;
10553             }
10554             return false;
10555         }
10556         
10557         if(value.length < this.minLength){
10558             return false;
10559         }
10560         if(value.length > this.maxLength){
10561             return false;
10562         }
10563         if(this.vtype){
10564             var vt = Roo.form.VTypes;
10565             if(!vt[this.vtype](value, this)){
10566                 return false;
10567             }
10568         }
10569         if(typeof this.validator == "function"){
10570             var msg = this.validator(value);
10571             if(msg !== true){
10572                 return false;
10573             }
10574             if (typeof(msg) == 'string') {
10575                 this.invalidText = msg;
10576             }
10577         }
10578         
10579         if(this.regex && !this.regex.test(value)){
10580             return false;
10581         }
10582         
10583         return true;
10584     },
10585     
10586      // private
10587     fireKey : function(e){
10588         //Roo.log('field ' + e.getKey());
10589         if(e.isNavKeyPress()){
10590             this.fireEvent("specialkey", this, e);
10591         }
10592     },
10593     focus : function (selectText){
10594         if(this.rendered){
10595             this.inputEl().focus();
10596             if(selectText === true){
10597                 this.inputEl().dom.select();
10598             }
10599         }
10600         return this;
10601     } ,
10602     
10603     onFocus : function(){
10604         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10605            // this.el.addClass(this.focusClass);
10606         }
10607         if(!this.hasFocus){
10608             this.hasFocus = true;
10609             this.startValue = this.getValue();
10610             this.fireEvent("focus", this);
10611         }
10612     },
10613     
10614     beforeBlur : Roo.emptyFn,
10615
10616     
10617     // private
10618     onBlur : function(){
10619         this.beforeBlur();
10620         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10621             //this.el.removeClass(this.focusClass);
10622         }
10623         this.hasFocus = false;
10624         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10625             this.validate();
10626         }
10627         var v = this.getValue();
10628         if(String(v) !== String(this.startValue)){
10629             this.fireEvent('change', this, v, this.startValue);
10630         }
10631         this.fireEvent("blur", this);
10632     },
10633     
10634     onChange : function(e)
10635     {
10636         var v = this.getValue();
10637         if(String(v) !== String(this.startValue)){
10638             this.fireEvent('change', this, v, this.startValue);
10639         }
10640         
10641     },
10642     
10643     /**
10644      * Resets the current field value to the originally loaded value and clears any validation messages
10645      */
10646     reset : function(){
10647         this.setValue(this.originalValue);
10648         this.validate();
10649     },
10650      /**
10651      * Returns the name of the field
10652      * @return {Mixed} name The name field
10653      */
10654     getName: function(){
10655         return this.name;
10656     },
10657      /**
10658      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10659      * @return {Mixed} value The field value
10660      */
10661     getValue : function(){
10662         
10663         var v = this.inputEl().getValue();
10664         
10665         return v;
10666     },
10667     /**
10668      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10669      * @return {Mixed} value The field value
10670      */
10671     getRawValue : function(){
10672         var v = this.inputEl().getValue();
10673         
10674         return v;
10675     },
10676     
10677     /**
10678      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10679      * @param {Mixed} value The value to set
10680      */
10681     setRawValue : function(v){
10682         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10683     },
10684     
10685     selectText : function(start, end){
10686         var v = this.getRawValue();
10687         if(v.length > 0){
10688             start = start === undefined ? 0 : start;
10689             end = end === undefined ? v.length : end;
10690             var d = this.inputEl().dom;
10691             if(d.setSelectionRange){
10692                 d.setSelectionRange(start, end);
10693             }else if(d.createTextRange){
10694                 var range = d.createTextRange();
10695                 range.moveStart("character", start);
10696                 range.moveEnd("character", v.length-end);
10697                 range.select();
10698             }
10699         }
10700     },
10701     
10702     /**
10703      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10704      * @param {Mixed} value The value to set
10705      */
10706     setValue : function(v){
10707         this.value = v;
10708         if(this.rendered){
10709             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10710             this.validate();
10711         }
10712     },
10713     
10714     /*
10715     processValue : function(value){
10716         if(this.stripCharsRe){
10717             var newValue = value.replace(this.stripCharsRe, '');
10718             if(newValue !== value){
10719                 this.setRawValue(newValue);
10720                 return newValue;
10721             }
10722         }
10723         return value;
10724     },
10725   */
10726     preFocus : function(){
10727         
10728         if(this.selectOnFocus){
10729             this.inputEl().dom.select();
10730         }
10731     },
10732     filterKeys : function(e){
10733         var k = e.getKey();
10734         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10735             return;
10736         }
10737         var c = e.getCharCode(), cc = String.fromCharCode(c);
10738         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10739             return;
10740         }
10741         if(!this.maskRe.test(cc)){
10742             e.stopEvent();
10743         }
10744     },
10745      /**
10746      * Clear any invalid styles/messages for this field
10747      */
10748     clearInvalid : function(){
10749         
10750         if(!this.el || this.preventMark){ // not rendered
10751             return;
10752         }
10753         
10754         
10755         this.el.removeClass([this.invalidClass, 'is-invalid']);
10756         
10757         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10758             
10759             var feedback = this.el.select('.form-control-feedback', true).first();
10760             
10761             if(feedback){
10762                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10763             }
10764             
10765         }
10766         
10767         if(this.indicator){
10768             this.indicator.removeClass('visible');
10769             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10770         }
10771         
10772         this.fireEvent('valid', this);
10773     },
10774     
10775      /**
10776      * Mark this field as valid
10777      */
10778     markValid : function()
10779     {
10780         if(!this.el  || this.preventMark){ // not rendered...
10781             return;
10782         }
10783         
10784         this.el.removeClass([this.invalidClass, this.validClass]);
10785         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10786
10787         var feedback = this.el.select('.form-control-feedback', true).first();
10788             
10789         if(feedback){
10790             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10791         }
10792         
10793         if(this.indicator){
10794             this.indicator.removeClass('visible');
10795             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10796         }
10797         
10798         if(this.disabled){
10799             return;
10800         }
10801         
10802         if(this.allowBlank && !this.getRawValue().length){
10803             return;
10804         }
10805         if (Roo.bootstrap.version == 3) {
10806             this.el.addClass(this.validClass);
10807         } else {
10808             this.inputEl().addClass('is-valid');
10809         }
10810
10811         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10812             
10813             var feedback = this.el.select('.form-control-feedback', true).first();
10814             
10815             if(feedback){
10816                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10817                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10818             }
10819             
10820         }
10821         
10822         this.fireEvent('valid', this);
10823     },
10824     
10825      /**
10826      * Mark this field as invalid
10827      * @param {String} msg The validation message
10828      */
10829     markInvalid : function(msg)
10830     {
10831         if(!this.el  || this.preventMark){ // not rendered
10832             return;
10833         }
10834         
10835         this.el.removeClass([this.invalidClass, this.validClass]);
10836         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10837         
10838         var feedback = this.el.select('.form-control-feedback', true).first();
10839             
10840         if(feedback){
10841             this.el.select('.form-control-feedback', true).first().removeClass(
10842                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10843         }
10844
10845         if(this.disabled){
10846             return;
10847         }
10848         
10849         if(this.allowBlank && !this.getRawValue().length){
10850             return;
10851         }
10852         
10853         if(this.indicator){
10854             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10855             this.indicator.addClass('visible');
10856         }
10857         if (Roo.bootstrap.version == 3) {
10858             this.el.addClass(this.invalidClass);
10859         } else {
10860             this.inputEl().addClass('is-invalid');
10861         }
10862         
10863         
10864         
10865         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10866             
10867             var feedback = this.el.select('.form-control-feedback', true).first();
10868             
10869             if(feedback){
10870                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10871                 
10872                 if(this.getValue().length || this.forceFeedback){
10873                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10874                 }
10875                 
10876             }
10877             
10878         }
10879         
10880         this.fireEvent('invalid', this, msg);
10881     },
10882     // private
10883     SafariOnKeyDown : function(event)
10884     {
10885         // this is a workaround for a password hang bug on chrome/ webkit.
10886         if (this.inputEl().dom.type != 'password') {
10887             return;
10888         }
10889         
10890         var isSelectAll = false;
10891         
10892         if(this.inputEl().dom.selectionEnd > 0){
10893             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10894         }
10895         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10896             event.preventDefault();
10897             this.setValue('');
10898             return;
10899         }
10900         
10901         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10902             
10903             event.preventDefault();
10904             // this is very hacky as keydown always get's upper case.
10905             //
10906             var cc = String.fromCharCode(event.getCharCode());
10907             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10908             
10909         }
10910     },
10911     adjustWidth : function(tag, w){
10912         tag = tag.toLowerCase();
10913         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10914             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10915                 if(tag == 'input'){
10916                     return w + 2;
10917                 }
10918                 if(tag == 'textarea'){
10919                     return w-2;
10920                 }
10921             }else if(Roo.isOpera){
10922                 if(tag == 'input'){
10923                     return w + 2;
10924                 }
10925                 if(tag == 'textarea'){
10926                     return w-2;
10927                 }
10928             }
10929         }
10930         return w;
10931     },
10932     
10933     setFieldLabel : function(v)
10934     {
10935         if(!this.rendered){
10936             return;
10937         }
10938         
10939         if(this.indicatorEl()){
10940             var ar = this.el.select('label > span',true);
10941             
10942             if (ar.elements.length) {
10943                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10944                 this.fieldLabel = v;
10945                 return;
10946             }
10947             
10948             var br = this.el.select('label',true);
10949             
10950             if(br.elements.length) {
10951                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10952                 this.fieldLabel = v;
10953                 return;
10954             }
10955             
10956             Roo.log('Cannot Found any of label > span || label in input');
10957             return;
10958         }
10959         
10960         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10961         this.fieldLabel = v;
10962         
10963         
10964     }
10965 });
10966
10967  
10968 /*
10969  * - LGPL
10970  *
10971  * Input
10972  * 
10973  */
10974
10975 /**
10976  * @class Roo.bootstrap.TextArea
10977  * @extends Roo.bootstrap.Input
10978  * Bootstrap TextArea class
10979  * @cfg {Number} cols Specifies the visible width of a text area
10980  * @cfg {Number} rows Specifies the visible number of lines in a text area
10981  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10982  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10983  * @cfg {string} html text
10984  * 
10985  * @constructor
10986  * Create a new TextArea
10987  * @param {Object} config The config object
10988  */
10989
10990 Roo.bootstrap.TextArea = function(config){
10991     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10992    
10993 };
10994
10995 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10996      
10997     cols : false,
10998     rows : 5,
10999     readOnly : false,
11000     warp : 'soft',
11001     resize : false,
11002     value: false,
11003     html: false,
11004     
11005     getAutoCreate : function(){
11006         
11007         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11008         
11009         var id = Roo.id();
11010         
11011         var cfg = {};
11012         
11013         if(this.inputType != 'hidden'){
11014             cfg.cls = 'form-group' //input-group
11015         }
11016         
11017         var input =  {
11018             tag: 'textarea',
11019             id : id,
11020             warp : this.warp,
11021             rows : this.rows,
11022             value : this.value || '',
11023             html: this.html || '',
11024             cls : 'form-control',
11025             placeholder : this.placeholder || '' 
11026             
11027         };
11028         
11029         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11030             input.maxLength = this.maxLength;
11031         }
11032         
11033         if(this.resize){
11034             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11035         }
11036         
11037         if(this.cols){
11038             input.cols = this.cols;
11039         }
11040         
11041         if (this.readOnly) {
11042             input.readonly = true;
11043         }
11044         
11045         if (this.name) {
11046             input.name = this.name;
11047         }
11048         
11049         if (this.size) {
11050             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11051         }
11052         
11053         var settings=this;
11054         ['xs','sm','md','lg'].map(function(size){
11055             if (settings[size]) {
11056                 cfg.cls += ' col-' + size + '-' + settings[size];
11057             }
11058         });
11059         
11060         var inputblock = input;
11061         
11062         if(this.hasFeedback && !this.allowBlank){
11063             
11064             var feedback = {
11065                 tag: 'span',
11066                 cls: 'glyphicon form-control-feedback'
11067             };
11068
11069             inputblock = {
11070                 cls : 'has-feedback',
11071                 cn :  [
11072                     input,
11073                     feedback
11074                 ] 
11075             };  
11076         }
11077         
11078         
11079         if (this.before || this.after) {
11080             
11081             inputblock = {
11082                 cls : 'input-group',
11083                 cn :  [] 
11084             };
11085             if (this.before) {
11086                 inputblock.cn.push({
11087                     tag :'span',
11088                     cls : 'input-group-addon',
11089                     html : this.before
11090                 });
11091             }
11092             
11093             inputblock.cn.push(input);
11094             
11095             if(this.hasFeedback && !this.allowBlank){
11096                 inputblock.cls += ' has-feedback';
11097                 inputblock.cn.push(feedback);
11098             }
11099             
11100             if (this.after) {
11101                 inputblock.cn.push({
11102                     tag :'span',
11103                     cls : 'input-group-addon',
11104                     html : this.after
11105                 });
11106             }
11107             
11108         }
11109         
11110         if (align ==='left' && this.fieldLabel.length) {
11111             cfg.cn = [
11112                 {
11113                     tag: 'label',
11114                     'for' :  id,
11115                     cls : 'control-label',
11116                     html : this.fieldLabel
11117                 },
11118                 {
11119                     cls : "",
11120                     cn: [
11121                         inputblock
11122                     ]
11123                 }
11124
11125             ];
11126             
11127             if(this.labelWidth > 12){
11128                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11129             }
11130
11131             if(this.labelWidth < 13 && this.labelmd == 0){
11132                 this.labelmd = this.labelWidth;
11133             }
11134
11135             if(this.labellg > 0){
11136                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11137                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11138             }
11139
11140             if(this.labelmd > 0){
11141                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11142                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11143             }
11144
11145             if(this.labelsm > 0){
11146                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11147                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11148             }
11149
11150             if(this.labelxs > 0){
11151                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11152                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11153             }
11154             
11155         } else if ( this.fieldLabel.length) {
11156             cfg.cn = [
11157
11158                {
11159                    tag: 'label',
11160                    //cls : 'input-group-addon',
11161                    html : this.fieldLabel
11162
11163                },
11164
11165                inputblock
11166
11167            ];
11168
11169         } else {
11170
11171             cfg.cn = [
11172
11173                 inputblock
11174
11175             ];
11176                 
11177         }
11178         
11179         if (this.disabled) {
11180             input.disabled=true;
11181         }
11182         
11183         return cfg;
11184         
11185     },
11186     /**
11187      * return the real textarea element.
11188      */
11189     inputEl: function ()
11190     {
11191         return this.el.select('textarea.form-control',true).first();
11192     },
11193     
11194     /**
11195      * Clear any invalid styles/messages for this field
11196      */
11197     clearInvalid : function()
11198     {
11199         
11200         if(!this.el || this.preventMark){ // not rendered
11201             return;
11202         }
11203         
11204         var label = this.el.select('label', true).first();
11205         var icon = this.el.select('i.fa-star', true).first();
11206         
11207         if(label && icon){
11208             icon.remove();
11209         }
11210         this.el.removeClass( this.validClass);
11211         this.inputEl().removeClass('is-invalid');
11212          
11213         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11214             
11215             var feedback = this.el.select('.form-control-feedback', true).first();
11216             
11217             if(feedback){
11218                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11219             }
11220             
11221         }
11222         
11223         this.fireEvent('valid', this);
11224     },
11225     
11226      /**
11227      * Mark this field as valid
11228      */
11229     markValid : function()
11230     {
11231         if(!this.el  || this.preventMark){ // not rendered
11232             return;
11233         }
11234         
11235         this.el.removeClass([this.invalidClass, this.validClass]);
11236         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11237         
11238         var feedback = this.el.select('.form-control-feedback', true).first();
11239             
11240         if(feedback){
11241             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11242         }
11243
11244         if(this.disabled || this.allowBlank){
11245             return;
11246         }
11247         
11248         var label = this.el.select('label', true).first();
11249         var icon = this.el.select('i.fa-star', true).first();
11250         
11251         if(label && icon){
11252             icon.remove();
11253         }
11254         if (Roo.bootstrap.version == 3) {
11255             this.el.addClass(this.validClass);
11256         } else {
11257             this.inputEl().addClass('is-valid');
11258         }
11259         
11260         
11261         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11262             
11263             var feedback = this.el.select('.form-control-feedback', true).first();
11264             
11265             if(feedback){
11266                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11267                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11268             }
11269             
11270         }
11271         
11272         this.fireEvent('valid', this);
11273     },
11274     
11275      /**
11276      * Mark this field as invalid
11277      * @param {String} msg The validation message
11278      */
11279     markInvalid : function(msg)
11280     {
11281         if(!this.el  || this.preventMark){ // not rendered
11282             return;
11283         }
11284         
11285         this.el.removeClass([this.invalidClass, this.validClass]);
11286         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11287         
11288         var feedback = this.el.select('.form-control-feedback', true).first();
11289             
11290         if(feedback){
11291             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11292         }
11293
11294         if(this.disabled || this.allowBlank){
11295             return;
11296         }
11297         
11298         var label = this.el.select('label', true).first();
11299         var icon = this.el.select('i.fa-star', true).first();
11300         
11301         if(!this.getValue().length && label && !icon){
11302             this.el.createChild({
11303                 tag : 'i',
11304                 cls : 'text-danger fa fa-lg fa-star',
11305                 tooltip : 'This field is required',
11306                 style : 'margin-right:5px;'
11307             }, label, true);
11308         }
11309         
11310         if (Roo.bootstrap.version == 3) {
11311             this.el.addClass(this.invalidClass);
11312         } else {
11313             this.inputEl().addClass('is-invalid');
11314         }
11315         
11316         // fixme ... this may be depricated need to test..
11317         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11318             
11319             var feedback = this.el.select('.form-control-feedback', true).first();
11320             
11321             if(feedback){
11322                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11323                 
11324                 if(this.getValue().length || this.forceFeedback){
11325                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11326                 }
11327                 
11328             }
11329             
11330         }
11331         
11332         this.fireEvent('invalid', this, msg);
11333     }
11334 });
11335
11336  
11337 /*
11338  * - LGPL
11339  *
11340  * trigger field - base class for combo..
11341  * 
11342  */
11343  
11344 /**
11345  * @class Roo.bootstrap.TriggerField
11346  * @extends Roo.bootstrap.Input
11347  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11348  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11349  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11350  * for which you can provide a custom implementation.  For example:
11351  * <pre><code>
11352 var trigger = new Roo.bootstrap.TriggerField();
11353 trigger.onTriggerClick = myTriggerFn;
11354 trigger.applyTo('my-field');
11355 </code></pre>
11356  *
11357  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11358  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11359  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11360  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11361  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11362
11363  * @constructor
11364  * Create a new TriggerField.
11365  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11366  * to the base TextField)
11367  */
11368 Roo.bootstrap.TriggerField = function(config){
11369     this.mimicing = false;
11370     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11371 };
11372
11373 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11374     /**
11375      * @cfg {String} triggerClass A CSS class to apply to the trigger
11376      */
11377      /**
11378      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11379      */
11380     hideTrigger:false,
11381
11382     /**
11383      * @cfg {Boolean} removable (true|false) special filter default false
11384      */
11385     removable : false,
11386     
11387     /** @cfg {Boolean} grow @hide */
11388     /** @cfg {Number} growMin @hide */
11389     /** @cfg {Number} growMax @hide */
11390
11391     /**
11392      * @hide 
11393      * @method
11394      */
11395     autoSize: Roo.emptyFn,
11396     // private
11397     monitorTab : true,
11398     // private
11399     deferHeight : true,
11400
11401     
11402     actionMode : 'wrap',
11403     
11404     caret : false,
11405     
11406     
11407     getAutoCreate : function(){
11408        
11409         var align = this.labelAlign || this.parentLabelAlign();
11410         
11411         var id = Roo.id();
11412         
11413         var cfg = {
11414             cls: 'form-group' //input-group
11415         };
11416         
11417         
11418         var input =  {
11419             tag: 'input',
11420             id : id,
11421             type : this.inputType,
11422             cls : 'form-control',
11423             autocomplete: 'new-password',
11424             placeholder : this.placeholder || '' 
11425             
11426         };
11427         if (this.name) {
11428             input.name = this.name;
11429         }
11430         if (this.size) {
11431             input.cls += ' input-' + this.size;
11432         }
11433         
11434         if (this.disabled) {
11435             input.disabled=true;
11436         }
11437         
11438         var inputblock = input;
11439         
11440         if(this.hasFeedback && !this.allowBlank){
11441             
11442             var feedback = {
11443                 tag: 'span',
11444                 cls: 'glyphicon form-control-feedback'
11445             };
11446             
11447             if(this.removable && !this.editable && !this.tickable){
11448                 inputblock = {
11449                     cls : 'has-feedback',
11450                     cn :  [
11451                         inputblock,
11452                         {
11453                             tag: 'button',
11454                             html : 'x',
11455                             cls : 'roo-combo-removable-btn close'
11456                         },
11457                         feedback
11458                     ] 
11459                 };
11460             } else {
11461                 inputblock = {
11462                     cls : 'has-feedback',
11463                     cn :  [
11464                         inputblock,
11465                         feedback
11466                     ] 
11467                 };
11468             }
11469
11470         } else {
11471             if(this.removable && !this.editable && !this.tickable){
11472                 inputblock = {
11473                     cls : 'roo-removable',
11474                     cn :  [
11475                         inputblock,
11476                         {
11477                             tag: 'button',
11478                             html : 'x',
11479                             cls : 'roo-combo-removable-btn close'
11480                         }
11481                     ] 
11482                 };
11483             }
11484         }
11485         
11486         if (this.before || this.after) {
11487             
11488             inputblock = {
11489                 cls : 'input-group',
11490                 cn :  [] 
11491             };
11492             if (this.before) {
11493                 inputblock.cn.push({
11494                     tag :'span',
11495                     cls : 'input-group-addon input-group-prepend input-group-text',
11496                     html : this.before
11497                 });
11498             }
11499             
11500             inputblock.cn.push(input);
11501             
11502             if(this.hasFeedback && !this.allowBlank){
11503                 inputblock.cls += ' has-feedback';
11504                 inputblock.cn.push(feedback);
11505             }
11506             
11507             if (this.after) {
11508                 inputblock.cn.push({
11509                     tag :'span',
11510                     cls : 'input-group-addon input-group-append input-group-text',
11511                     html : this.after
11512                 });
11513             }
11514             
11515         };
11516         
11517       
11518         
11519         var ibwrap = inputblock;
11520         
11521         if(this.multiple){
11522             ibwrap = {
11523                 tag: 'ul',
11524                 cls: 'roo-select2-choices',
11525                 cn:[
11526                     {
11527                         tag: 'li',
11528                         cls: 'roo-select2-search-field',
11529                         cn: [
11530
11531                             inputblock
11532                         ]
11533                     }
11534                 ]
11535             };
11536                 
11537         }
11538         
11539         var combobox = {
11540             cls: 'roo-select2-container input-group',
11541             cn: [
11542                  {
11543                     tag: 'input',
11544                     type : 'hidden',
11545                     cls: 'form-hidden-field'
11546                 },
11547                 ibwrap
11548             ]
11549         };
11550         
11551         if(!this.multiple && this.showToggleBtn){
11552             
11553             var caret = {
11554                         tag: 'span',
11555                         cls: 'caret'
11556              };
11557             if (this.caret != false) {
11558                 caret = {
11559                      tag: 'i',
11560                      cls: 'fa fa-' + this.caret
11561                 };
11562                 
11563             }
11564             
11565             combobox.cn.push({
11566                 tag :'span',
11567                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11568                 cn : [
11569                     Roo.bootstrap.version == 3 ? caret : '',
11570                     {
11571                         tag: 'span',
11572                         cls: 'combobox-clear',
11573                         cn  : [
11574                             {
11575                                 tag : 'i',
11576                                 cls: 'icon-remove'
11577                             }
11578                         ]
11579                     }
11580                 ]
11581
11582             })
11583         }
11584         
11585         if(this.multiple){
11586             combobox.cls += ' roo-select2-container-multi';
11587         }
11588          var indicator = {
11589             tag : 'i',
11590             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11591             tooltip : 'This field is required'
11592         };
11593         if (Roo.bootstrap.version == 4) {
11594             indicator = {
11595                 tag : 'i',
11596                 style : 'display:none'
11597             };
11598         }
11599         
11600         
11601         if (align ==='left' && this.fieldLabel.length) {
11602             
11603             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11604
11605             cfg.cn = [
11606                 indicator,
11607                 {
11608                     tag: 'label',
11609                     'for' :  id,
11610                     cls : 'control-label',
11611                     html : this.fieldLabel
11612
11613                 },
11614                 {
11615                     cls : "", 
11616                     cn: [
11617                         combobox
11618                     ]
11619                 }
11620
11621             ];
11622             
11623             var labelCfg = cfg.cn[1];
11624             var contentCfg = cfg.cn[2];
11625             
11626             if(this.indicatorpos == 'right'){
11627                 cfg.cn = [
11628                     {
11629                         tag: 'label',
11630                         'for' :  id,
11631                         cls : 'control-label',
11632                         cn : [
11633                             {
11634                                 tag : 'span',
11635                                 html : this.fieldLabel
11636                             },
11637                             indicator
11638                         ]
11639                     },
11640                     {
11641                         cls : "", 
11642                         cn: [
11643                             combobox
11644                         ]
11645                     }
11646
11647                 ];
11648                 
11649                 labelCfg = cfg.cn[0];
11650                 contentCfg = cfg.cn[1];
11651             }
11652             
11653             if(this.labelWidth > 12){
11654                 labelCfg.style = "width: " + this.labelWidth + 'px';
11655             }
11656             
11657             if(this.labelWidth < 13 && this.labelmd == 0){
11658                 this.labelmd = this.labelWidth;
11659             }
11660             
11661             if(this.labellg > 0){
11662                 labelCfg.cls += ' col-lg-' + this.labellg;
11663                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11664             }
11665             
11666             if(this.labelmd > 0){
11667                 labelCfg.cls += ' col-md-' + this.labelmd;
11668                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11669             }
11670             
11671             if(this.labelsm > 0){
11672                 labelCfg.cls += ' col-sm-' + this.labelsm;
11673                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11674             }
11675             
11676             if(this.labelxs > 0){
11677                 labelCfg.cls += ' col-xs-' + this.labelxs;
11678                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11679             }
11680             
11681         } else if ( this.fieldLabel.length) {
11682 //                Roo.log(" label");
11683             cfg.cn = [
11684                 indicator,
11685                {
11686                    tag: 'label',
11687                    //cls : 'input-group-addon',
11688                    html : this.fieldLabel
11689
11690                },
11691
11692                combobox
11693
11694             ];
11695             
11696             if(this.indicatorpos == 'right'){
11697                 
11698                 cfg.cn = [
11699                     {
11700                        tag: 'label',
11701                        cn : [
11702                            {
11703                                tag : 'span',
11704                                html : this.fieldLabel
11705                            },
11706                            indicator
11707                        ]
11708
11709                     },
11710                     combobox
11711
11712                 ];
11713
11714             }
11715
11716         } else {
11717             
11718 //                Roo.log(" no label && no align");
11719                 cfg = combobox
11720                      
11721                 
11722         }
11723         
11724         var settings=this;
11725         ['xs','sm','md','lg'].map(function(size){
11726             if (settings[size]) {
11727                 cfg.cls += ' col-' + size + '-' + settings[size];
11728             }
11729         });
11730         
11731         return cfg;
11732         
11733     },
11734     
11735     
11736     
11737     // private
11738     onResize : function(w, h){
11739 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11740 //        if(typeof w == 'number'){
11741 //            var x = w - this.trigger.getWidth();
11742 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11743 //            this.trigger.setStyle('left', x+'px');
11744 //        }
11745     },
11746
11747     // private
11748     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11749
11750     // private
11751     getResizeEl : function(){
11752         return this.inputEl();
11753     },
11754
11755     // private
11756     getPositionEl : function(){
11757         return this.inputEl();
11758     },
11759
11760     // private
11761     alignErrorIcon : function(){
11762         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11763     },
11764
11765     // private
11766     initEvents : function(){
11767         
11768         this.createList();
11769         
11770         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11771         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11772         if(!this.multiple && this.showToggleBtn){
11773             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11774             if(this.hideTrigger){
11775                 this.trigger.setDisplayed(false);
11776             }
11777             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11778         }
11779         
11780         if(this.multiple){
11781             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11782         }
11783         
11784         if(this.removable && !this.editable && !this.tickable){
11785             var close = this.closeTriggerEl();
11786             
11787             if(close){
11788                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11789                 close.on('click', this.removeBtnClick, this, close);
11790             }
11791         }
11792         
11793         //this.trigger.addClassOnOver('x-form-trigger-over');
11794         //this.trigger.addClassOnClick('x-form-trigger-click');
11795         
11796         //if(!this.width){
11797         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11798         //}
11799     },
11800     
11801     closeTriggerEl : function()
11802     {
11803         var close = this.el.select('.roo-combo-removable-btn', true).first();
11804         return close ? close : false;
11805     },
11806     
11807     removeBtnClick : function(e, h, el)
11808     {
11809         e.preventDefault();
11810         
11811         if(this.fireEvent("remove", this) !== false){
11812             this.reset();
11813             this.fireEvent("afterremove", this)
11814         }
11815     },
11816     
11817     createList : function()
11818     {
11819         this.list = Roo.get(document.body).createChild({
11820             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11821             cls: 'typeahead typeahead-long dropdown-menu',
11822             style: 'display:none'
11823         });
11824         
11825         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11826         
11827     },
11828
11829     // private
11830     initTrigger : function(){
11831        
11832     },
11833
11834     // private
11835     onDestroy : function(){
11836         if(this.trigger){
11837             this.trigger.removeAllListeners();
11838           //  this.trigger.remove();
11839         }
11840         //if(this.wrap){
11841         //    this.wrap.remove();
11842         //}
11843         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11844     },
11845
11846     // private
11847     onFocus : function(){
11848         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11849         /*
11850         if(!this.mimicing){
11851             this.wrap.addClass('x-trigger-wrap-focus');
11852             this.mimicing = true;
11853             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11854             if(this.monitorTab){
11855                 this.el.on("keydown", this.checkTab, this);
11856             }
11857         }
11858         */
11859     },
11860
11861     // private
11862     checkTab : function(e){
11863         if(e.getKey() == e.TAB){
11864             this.triggerBlur();
11865         }
11866     },
11867
11868     // private
11869     onBlur : function(){
11870         // do nothing
11871     },
11872
11873     // private
11874     mimicBlur : function(e, t){
11875         /*
11876         if(!this.wrap.contains(t) && this.validateBlur()){
11877             this.triggerBlur();
11878         }
11879         */
11880     },
11881
11882     // private
11883     triggerBlur : function(){
11884         this.mimicing = false;
11885         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11886         if(this.monitorTab){
11887             this.el.un("keydown", this.checkTab, this);
11888         }
11889         //this.wrap.removeClass('x-trigger-wrap-focus');
11890         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11891     },
11892
11893     // private
11894     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11895     validateBlur : function(e, t){
11896         return true;
11897     },
11898
11899     // private
11900     onDisable : function(){
11901         this.inputEl().dom.disabled = true;
11902         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11903         //if(this.wrap){
11904         //    this.wrap.addClass('x-item-disabled');
11905         //}
11906     },
11907
11908     // private
11909     onEnable : function(){
11910         this.inputEl().dom.disabled = false;
11911         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11912         //if(this.wrap){
11913         //    this.el.removeClass('x-item-disabled');
11914         //}
11915     },
11916
11917     // private
11918     onShow : function(){
11919         var ae = this.getActionEl();
11920         
11921         if(ae){
11922             ae.dom.style.display = '';
11923             ae.dom.style.visibility = 'visible';
11924         }
11925     },
11926
11927     // private
11928     
11929     onHide : function(){
11930         var ae = this.getActionEl();
11931         ae.dom.style.display = 'none';
11932     },
11933
11934     /**
11935      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11936      * by an implementing function.
11937      * @method
11938      * @param {EventObject} e
11939      */
11940     onTriggerClick : Roo.emptyFn
11941 });
11942  /*
11943  * Based on:
11944  * Ext JS Library 1.1.1
11945  * Copyright(c) 2006-2007, Ext JS, LLC.
11946  *
11947  * Originally Released Under LGPL - original licence link has changed is not relivant.
11948  *
11949  * Fork - LGPL
11950  * <script type="text/javascript">
11951  */
11952
11953
11954 /**
11955  * @class Roo.data.SortTypes
11956  * @singleton
11957  * Defines the default sorting (casting?) comparison functions used when sorting data.
11958  */
11959 Roo.data.SortTypes = {
11960     /**
11961      * Default sort that does nothing
11962      * @param {Mixed} s The value being converted
11963      * @return {Mixed} The comparison value
11964      */
11965     none : function(s){
11966         return s;
11967     },
11968     
11969     /**
11970      * The regular expression used to strip tags
11971      * @type {RegExp}
11972      * @property
11973      */
11974     stripTagsRE : /<\/?[^>]+>/gi,
11975     
11976     /**
11977      * Strips all HTML tags to sort on text only
11978      * @param {Mixed} s The value being converted
11979      * @return {String} The comparison value
11980      */
11981     asText : function(s){
11982         return String(s).replace(this.stripTagsRE, "");
11983     },
11984     
11985     /**
11986      * Strips all HTML tags to sort on text only - Case insensitive
11987      * @param {Mixed} s The value being converted
11988      * @return {String} The comparison value
11989      */
11990     asUCText : function(s){
11991         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11992     },
11993     
11994     /**
11995      * Case insensitive string
11996      * @param {Mixed} s The value being converted
11997      * @return {String} The comparison value
11998      */
11999     asUCString : function(s) {
12000         return String(s).toUpperCase();
12001     },
12002     
12003     /**
12004      * Date sorting
12005      * @param {Mixed} s The value being converted
12006      * @return {Number} The comparison value
12007      */
12008     asDate : function(s) {
12009         if(!s){
12010             return 0;
12011         }
12012         if(s instanceof Date){
12013             return s.getTime();
12014         }
12015         return Date.parse(String(s));
12016     },
12017     
12018     /**
12019      * Float sorting
12020      * @param {Mixed} s The value being converted
12021      * @return {Float} The comparison value
12022      */
12023     asFloat : function(s) {
12024         var val = parseFloat(String(s).replace(/,/g, ""));
12025         if(isNaN(val)) {
12026             val = 0;
12027         }
12028         return val;
12029     },
12030     
12031     /**
12032      * Integer sorting
12033      * @param {Mixed} s The value being converted
12034      * @return {Number} The comparison value
12035      */
12036     asInt : function(s) {
12037         var val = parseInt(String(s).replace(/,/g, ""));
12038         if(isNaN(val)) {
12039             val = 0;
12040         }
12041         return val;
12042     }
12043 };/*
12044  * Based on:
12045  * Ext JS Library 1.1.1
12046  * Copyright(c) 2006-2007, Ext JS, LLC.
12047  *
12048  * Originally Released Under LGPL - original licence link has changed is not relivant.
12049  *
12050  * Fork - LGPL
12051  * <script type="text/javascript">
12052  */
12053
12054 /**
12055 * @class Roo.data.Record
12056  * Instances of this class encapsulate both record <em>definition</em> information, and record
12057  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12058  * to access Records cached in an {@link Roo.data.Store} object.<br>
12059  * <p>
12060  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12061  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12062  * objects.<br>
12063  * <p>
12064  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12065  * @constructor
12066  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12067  * {@link #create}. The parameters are the same.
12068  * @param {Array} data An associative Array of data values keyed by the field name.
12069  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12070  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12071  * not specified an integer id is generated.
12072  */
12073 Roo.data.Record = function(data, id){
12074     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12075     this.data = data;
12076 };
12077
12078 /**
12079  * Generate a constructor for a specific record layout.
12080  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12081  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12082  * Each field definition object may contain the following properties: <ul>
12083  * <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,
12084  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12085  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12086  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12087  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12088  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12089  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12090  * this may be omitted.</p></li>
12091  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12092  * <ul><li>auto (Default, implies no conversion)</li>
12093  * <li>string</li>
12094  * <li>int</li>
12095  * <li>float</li>
12096  * <li>boolean</li>
12097  * <li>date</li></ul></p></li>
12098  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12099  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12100  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12101  * by the Reader into an object that will be stored in the Record. It is passed the
12102  * following parameters:<ul>
12103  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12104  * </ul></p></li>
12105  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12106  * </ul>
12107  * <br>usage:<br><pre><code>
12108 var TopicRecord = Roo.data.Record.create(
12109     {name: 'title', mapping: 'topic_title'},
12110     {name: 'author', mapping: 'username'},
12111     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12112     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12113     {name: 'lastPoster', mapping: 'user2'},
12114     {name: 'excerpt', mapping: 'post_text'}
12115 );
12116
12117 var myNewRecord = new TopicRecord({
12118     title: 'Do my job please',
12119     author: 'noobie',
12120     totalPosts: 1,
12121     lastPost: new Date(),
12122     lastPoster: 'Animal',
12123     excerpt: 'No way dude!'
12124 });
12125 myStore.add(myNewRecord);
12126 </code></pre>
12127  * @method create
12128  * @static
12129  */
12130 Roo.data.Record.create = function(o){
12131     var f = function(){
12132         f.superclass.constructor.apply(this, arguments);
12133     };
12134     Roo.extend(f, Roo.data.Record);
12135     var p = f.prototype;
12136     p.fields = new Roo.util.MixedCollection(false, function(field){
12137         return field.name;
12138     });
12139     for(var i = 0, len = o.length; i < len; i++){
12140         p.fields.add(new Roo.data.Field(o[i]));
12141     }
12142     f.getField = function(name){
12143         return p.fields.get(name);  
12144     };
12145     return f;
12146 };
12147
12148 Roo.data.Record.AUTO_ID = 1000;
12149 Roo.data.Record.EDIT = 'edit';
12150 Roo.data.Record.REJECT = 'reject';
12151 Roo.data.Record.COMMIT = 'commit';
12152
12153 Roo.data.Record.prototype = {
12154     /**
12155      * Readonly flag - true if this record has been modified.
12156      * @type Boolean
12157      */
12158     dirty : false,
12159     editing : false,
12160     error: null,
12161     modified: null,
12162
12163     // private
12164     join : function(store){
12165         this.store = store;
12166     },
12167
12168     /**
12169      * Set the named field to the specified value.
12170      * @param {String} name The name of the field to set.
12171      * @param {Object} value The value to set the field to.
12172      */
12173     set : function(name, value){
12174         if(this.data[name] == value){
12175             return;
12176         }
12177         this.dirty = true;
12178         if(!this.modified){
12179             this.modified = {};
12180         }
12181         if(typeof this.modified[name] == 'undefined'){
12182             this.modified[name] = this.data[name];
12183         }
12184         this.data[name] = value;
12185         if(!this.editing && this.store){
12186             this.store.afterEdit(this);
12187         }       
12188     },
12189
12190     /**
12191      * Get the value of the named field.
12192      * @param {String} name The name of the field to get the value of.
12193      * @return {Object} The value of the field.
12194      */
12195     get : function(name){
12196         return this.data[name]; 
12197     },
12198
12199     // private
12200     beginEdit : function(){
12201         this.editing = true;
12202         this.modified = {}; 
12203     },
12204
12205     // private
12206     cancelEdit : function(){
12207         this.editing = false;
12208         delete this.modified;
12209     },
12210
12211     // private
12212     endEdit : function(){
12213         this.editing = false;
12214         if(this.dirty && this.store){
12215             this.store.afterEdit(this);
12216         }
12217     },
12218
12219     /**
12220      * Usually called by the {@link Roo.data.Store} which owns the Record.
12221      * Rejects all changes made to the Record since either creation, or the last commit operation.
12222      * Modified fields are reverted to their original values.
12223      * <p>
12224      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12225      * of reject operations.
12226      */
12227     reject : function(){
12228         var m = this.modified;
12229         for(var n in m){
12230             if(typeof m[n] != "function"){
12231                 this.data[n] = m[n];
12232             }
12233         }
12234         this.dirty = false;
12235         delete this.modified;
12236         this.editing = false;
12237         if(this.store){
12238             this.store.afterReject(this);
12239         }
12240     },
12241
12242     /**
12243      * Usually called by the {@link Roo.data.Store} which owns the Record.
12244      * Commits all changes made to the Record since either creation, or the last commit operation.
12245      * <p>
12246      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12247      * of commit operations.
12248      */
12249     commit : function(){
12250         this.dirty = false;
12251         delete this.modified;
12252         this.editing = false;
12253         if(this.store){
12254             this.store.afterCommit(this);
12255         }
12256     },
12257
12258     // private
12259     hasError : function(){
12260         return this.error != null;
12261     },
12262
12263     // private
12264     clearError : function(){
12265         this.error = null;
12266     },
12267
12268     /**
12269      * Creates a copy of this record.
12270      * @param {String} id (optional) A new record id if you don't want to use this record's id
12271      * @return {Record}
12272      */
12273     copy : function(newId) {
12274         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12275     }
12276 };/*
12277  * Based on:
12278  * Ext JS Library 1.1.1
12279  * Copyright(c) 2006-2007, Ext JS, LLC.
12280  *
12281  * Originally Released Under LGPL - original licence link has changed is not relivant.
12282  *
12283  * Fork - LGPL
12284  * <script type="text/javascript">
12285  */
12286
12287
12288
12289 /**
12290  * @class Roo.data.Store
12291  * @extends Roo.util.Observable
12292  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12293  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12294  * <p>
12295  * 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
12296  * has no knowledge of the format of the data returned by the Proxy.<br>
12297  * <p>
12298  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12299  * instances from the data object. These records are cached and made available through accessor functions.
12300  * @constructor
12301  * Creates a new Store.
12302  * @param {Object} config A config object containing the objects needed for the Store to access data,
12303  * and read the data into Records.
12304  */
12305 Roo.data.Store = function(config){
12306     this.data = new Roo.util.MixedCollection(false);
12307     this.data.getKey = function(o){
12308         return o.id;
12309     };
12310     this.baseParams = {};
12311     // private
12312     this.paramNames = {
12313         "start" : "start",
12314         "limit" : "limit",
12315         "sort" : "sort",
12316         "dir" : "dir",
12317         "multisort" : "_multisort"
12318     };
12319
12320     if(config && config.data){
12321         this.inlineData = config.data;
12322         delete config.data;
12323     }
12324
12325     Roo.apply(this, config);
12326     
12327     if(this.reader){ // reader passed
12328         this.reader = Roo.factory(this.reader, Roo.data);
12329         this.reader.xmodule = this.xmodule || false;
12330         if(!this.recordType){
12331             this.recordType = this.reader.recordType;
12332         }
12333         if(this.reader.onMetaChange){
12334             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12335         }
12336     }
12337
12338     if(this.recordType){
12339         this.fields = this.recordType.prototype.fields;
12340     }
12341     this.modified = [];
12342
12343     this.addEvents({
12344         /**
12345          * @event datachanged
12346          * Fires when the data cache has changed, and a widget which is using this Store
12347          * as a Record cache should refresh its view.
12348          * @param {Store} this
12349          */
12350         datachanged : true,
12351         /**
12352          * @event metachange
12353          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12354          * @param {Store} this
12355          * @param {Object} meta The JSON metadata
12356          */
12357         metachange : true,
12358         /**
12359          * @event add
12360          * Fires when Records have been added to the Store
12361          * @param {Store} this
12362          * @param {Roo.data.Record[]} records The array of Records added
12363          * @param {Number} index The index at which the record(s) were added
12364          */
12365         add : true,
12366         /**
12367          * @event remove
12368          * Fires when a Record has been removed from the Store
12369          * @param {Store} this
12370          * @param {Roo.data.Record} record The Record that was removed
12371          * @param {Number} index The index at which the record was removed
12372          */
12373         remove : true,
12374         /**
12375          * @event update
12376          * Fires when a Record has been updated
12377          * @param {Store} this
12378          * @param {Roo.data.Record} record The Record that was updated
12379          * @param {String} operation The update operation being performed.  Value may be one of:
12380          * <pre><code>
12381  Roo.data.Record.EDIT
12382  Roo.data.Record.REJECT
12383  Roo.data.Record.COMMIT
12384          * </code></pre>
12385          */
12386         update : true,
12387         /**
12388          * @event clear
12389          * Fires when the data cache has been cleared.
12390          * @param {Store} this
12391          */
12392         clear : true,
12393         /**
12394          * @event beforeload
12395          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12396          * the load action will be canceled.
12397          * @param {Store} this
12398          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12399          */
12400         beforeload : true,
12401         /**
12402          * @event beforeloadadd
12403          * Fires after a new set of Records has been loaded.
12404          * @param {Store} this
12405          * @param {Roo.data.Record[]} records The Records that were loaded
12406          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12407          */
12408         beforeloadadd : true,
12409         /**
12410          * @event load
12411          * Fires after a new set of Records has been loaded, before they are added to the store.
12412          * @param {Store} this
12413          * @param {Roo.data.Record[]} records The Records that were loaded
12414          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12415          * @params {Object} return from reader
12416          */
12417         load : true,
12418         /**
12419          * @event loadexception
12420          * Fires if an exception occurs in the Proxy during loading.
12421          * Called with the signature of the Proxy's "loadexception" event.
12422          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12423          * 
12424          * @param {Proxy} 
12425          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12426          * @param {Object} load options 
12427          * @param {Object} jsonData from your request (normally this contains the Exception)
12428          */
12429         loadexception : true
12430     });
12431     
12432     if(this.proxy){
12433         this.proxy = Roo.factory(this.proxy, Roo.data);
12434         this.proxy.xmodule = this.xmodule || false;
12435         this.relayEvents(this.proxy,  ["loadexception"]);
12436     }
12437     this.sortToggle = {};
12438     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12439
12440     Roo.data.Store.superclass.constructor.call(this);
12441
12442     if(this.inlineData){
12443         this.loadData(this.inlineData);
12444         delete this.inlineData;
12445     }
12446 };
12447
12448 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12449      /**
12450     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12451     * without a remote query - used by combo/forms at present.
12452     */
12453     
12454     /**
12455     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12456     */
12457     /**
12458     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12459     */
12460     /**
12461     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12462     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12463     */
12464     /**
12465     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12466     * on any HTTP request
12467     */
12468     /**
12469     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12470     */
12471     /**
12472     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12473     */
12474     multiSort: false,
12475     /**
12476     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12477     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12478     */
12479     remoteSort : false,
12480
12481     /**
12482     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12483      * loaded or when a record is removed. (defaults to false).
12484     */
12485     pruneModifiedRecords : false,
12486
12487     // private
12488     lastOptions : null,
12489
12490     /**
12491      * Add Records to the Store and fires the add event.
12492      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12493      */
12494     add : function(records){
12495         records = [].concat(records);
12496         for(var i = 0, len = records.length; i < len; i++){
12497             records[i].join(this);
12498         }
12499         var index = this.data.length;
12500         this.data.addAll(records);
12501         this.fireEvent("add", this, records, index);
12502     },
12503
12504     /**
12505      * Remove a Record from the Store and fires the remove event.
12506      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12507      */
12508     remove : function(record){
12509         var index = this.data.indexOf(record);
12510         this.data.removeAt(index);
12511  
12512         if(this.pruneModifiedRecords){
12513             this.modified.remove(record);
12514         }
12515         this.fireEvent("remove", this, record, index);
12516     },
12517
12518     /**
12519      * Remove all Records from the Store and fires the clear event.
12520      */
12521     removeAll : function(){
12522         this.data.clear();
12523         if(this.pruneModifiedRecords){
12524             this.modified = [];
12525         }
12526         this.fireEvent("clear", this);
12527     },
12528
12529     /**
12530      * Inserts Records to the Store at the given index and fires the add event.
12531      * @param {Number} index The start index at which to insert the passed Records.
12532      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12533      */
12534     insert : function(index, records){
12535         records = [].concat(records);
12536         for(var i = 0, len = records.length; i < len; i++){
12537             this.data.insert(index, records[i]);
12538             records[i].join(this);
12539         }
12540         this.fireEvent("add", this, records, index);
12541     },
12542
12543     /**
12544      * Get the index within the cache of the passed Record.
12545      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12546      * @return {Number} The index of the passed Record. Returns -1 if not found.
12547      */
12548     indexOf : function(record){
12549         return this.data.indexOf(record);
12550     },
12551
12552     /**
12553      * Get the index within the cache of the Record with the passed id.
12554      * @param {String} id The id of the Record to find.
12555      * @return {Number} The index of the Record. Returns -1 if not found.
12556      */
12557     indexOfId : function(id){
12558         return this.data.indexOfKey(id);
12559     },
12560
12561     /**
12562      * Get the Record with the specified id.
12563      * @param {String} id The id of the Record to find.
12564      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12565      */
12566     getById : function(id){
12567         return this.data.key(id);
12568     },
12569
12570     /**
12571      * Get the Record at the specified index.
12572      * @param {Number} index The index of the Record to find.
12573      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12574      */
12575     getAt : function(index){
12576         return this.data.itemAt(index);
12577     },
12578
12579     /**
12580      * Returns a range of Records between specified indices.
12581      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12582      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12583      * @return {Roo.data.Record[]} An array of Records
12584      */
12585     getRange : function(start, end){
12586         return this.data.getRange(start, end);
12587     },
12588
12589     // private
12590     storeOptions : function(o){
12591         o = Roo.apply({}, o);
12592         delete o.callback;
12593         delete o.scope;
12594         this.lastOptions = o;
12595     },
12596
12597     /**
12598      * Loads the Record cache from the configured Proxy using the configured Reader.
12599      * <p>
12600      * If using remote paging, then the first load call must specify the <em>start</em>
12601      * and <em>limit</em> properties in the options.params property to establish the initial
12602      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12603      * <p>
12604      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12605      * and this call will return before the new data has been loaded. Perform any post-processing
12606      * in a callback function, or in a "load" event handler.</strong>
12607      * <p>
12608      * @param {Object} options An object containing properties which control loading options:<ul>
12609      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12610      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12611      * passed the following arguments:<ul>
12612      * <li>r : Roo.data.Record[]</li>
12613      * <li>options: Options object from the load call</li>
12614      * <li>success: Boolean success indicator</li></ul></li>
12615      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12616      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12617      * </ul>
12618      */
12619     load : function(options){
12620         options = options || {};
12621         if(this.fireEvent("beforeload", this, options) !== false){
12622             this.storeOptions(options);
12623             var p = Roo.apply(options.params || {}, this.baseParams);
12624             // if meta was not loaded from remote source.. try requesting it.
12625             if (!this.reader.metaFromRemote) {
12626                 p._requestMeta = 1;
12627             }
12628             if(this.sortInfo && this.remoteSort){
12629                 var pn = this.paramNames;
12630                 p[pn["sort"]] = this.sortInfo.field;
12631                 p[pn["dir"]] = this.sortInfo.direction;
12632             }
12633             if (this.multiSort) {
12634                 var pn = this.paramNames;
12635                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12636             }
12637             
12638             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12639         }
12640     },
12641
12642     /**
12643      * Reloads the Record cache from the configured Proxy using the configured Reader and
12644      * the options from the last load operation performed.
12645      * @param {Object} options (optional) An object containing properties which may override the options
12646      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12647      * the most recently used options are reused).
12648      */
12649     reload : function(options){
12650         this.load(Roo.applyIf(options||{}, this.lastOptions));
12651     },
12652
12653     // private
12654     // Called as a callback by the Reader during a load operation.
12655     loadRecords : function(o, options, success){
12656         if(!o || success === false){
12657             if(success !== false){
12658                 this.fireEvent("load", this, [], options, o);
12659             }
12660             if(options.callback){
12661                 options.callback.call(options.scope || this, [], options, false);
12662             }
12663             return;
12664         }
12665         // if data returned failure - throw an exception.
12666         if (o.success === false) {
12667             // show a message if no listener is registered.
12668             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12669                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12670             }
12671             // loadmask wil be hooked into this..
12672             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12673             return;
12674         }
12675         var r = o.records, t = o.totalRecords || r.length;
12676         
12677         this.fireEvent("beforeloadadd", this, r, options, o);
12678         
12679         if(!options || options.add !== true){
12680             if(this.pruneModifiedRecords){
12681                 this.modified = [];
12682             }
12683             for(var i = 0, len = r.length; i < len; i++){
12684                 r[i].join(this);
12685             }
12686             if(this.snapshot){
12687                 this.data = this.snapshot;
12688                 delete this.snapshot;
12689             }
12690             this.data.clear();
12691             this.data.addAll(r);
12692             this.totalLength = t;
12693             this.applySort();
12694             this.fireEvent("datachanged", this);
12695         }else{
12696             this.totalLength = Math.max(t, this.data.length+r.length);
12697             this.add(r);
12698         }
12699         
12700         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12701                 
12702             var e = new Roo.data.Record({});
12703
12704             e.set(this.parent.displayField, this.parent.emptyTitle);
12705             e.set(this.parent.valueField, '');
12706
12707             this.insert(0, e);
12708         }
12709             
12710         this.fireEvent("load", this, r, options, o);
12711         if(options.callback){
12712             options.callback.call(options.scope || this, r, options, true);
12713         }
12714     },
12715
12716
12717     /**
12718      * Loads data from a passed data block. A Reader which understands the format of the data
12719      * must have been configured in the constructor.
12720      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12721      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12722      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12723      */
12724     loadData : function(o, append){
12725         var r = this.reader.readRecords(o);
12726         this.loadRecords(r, {add: append}, true);
12727     },
12728     
12729      /**
12730      * using 'cn' the nested child reader read the child array into it's child stores.
12731      * @param {Object} rec The record with a 'children array
12732      */
12733     loadDataFromChildren : function(rec)
12734     {
12735         this.loadData(this.reader.toLoadData(rec));
12736     },
12737     
12738
12739     /**
12740      * Gets the number of cached records.
12741      * <p>
12742      * <em>If using paging, this may not be the total size of the dataset. If the data object
12743      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12744      * the data set size</em>
12745      */
12746     getCount : function(){
12747         return this.data.length || 0;
12748     },
12749
12750     /**
12751      * Gets the total number of records in the dataset as returned by the server.
12752      * <p>
12753      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12754      * the dataset size</em>
12755      */
12756     getTotalCount : function(){
12757         return this.totalLength || 0;
12758     },
12759
12760     /**
12761      * Returns the sort state of the Store as an object with two properties:
12762      * <pre><code>
12763  field {String} The name of the field by which the Records are sorted
12764  direction {String} The sort order, "ASC" or "DESC"
12765      * </code></pre>
12766      */
12767     getSortState : function(){
12768         return this.sortInfo;
12769     },
12770
12771     // private
12772     applySort : function(){
12773         if(this.sortInfo && !this.remoteSort){
12774             var s = this.sortInfo, f = s.field;
12775             var st = this.fields.get(f).sortType;
12776             var fn = function(r1, r2){
12777                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12778                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12779             };
12780             this.data.sort(s.direction, fn);
12781             if(this.snapshot && this.snapshot != this.data){
12782                 this.snapshot.sort(s.direction, fn);
12783             }
12784         }
12785     },
12786
12787     /**
12788      * Sets the default sort column and order to be used by the next load operation.
12789      * @param {String} fieldName The name of the field to sort by.
12790      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12791      */
12792     setDefaultSort : function(field, dir){
12793         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12794     },
12795
12796     /**
12797      * Sort the Records.
12798      * If remote sorting is used, the sort is performed on the server, and the cache is
12799      * reloaded. If local sorting is used, the cache is sorted internally.
12800      * @param {String} fieldName The name of the field to sort by.
12801      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12802      */
12803     sort : function(fieldName, dir){
12804         var f = this.fields.get(fieldName);
12805         if(!dir){
12806             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12807             
12808             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12809                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12810             }else{
12811                 dir = f.sortDir;
12812             }
12813         }
12814         this.sortToggle[f.name] = dir;
12815         this.sortInfo = {field: f.name, direction: dir};
12816         if(!this.remoteSort){
12817             this.applySort();
12818             this.fireEvent("datachanged", this);
12819         }else{
12820             this.load(this.lastOptions);
12821         }
12822     },
12823
12824     /**
12825      * Calls the specified function for each of the Records in the cache.
12826      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12827      * Returning <em>false</em> aborts and exits the iteration.
12828      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12829      */
12830     each : function(fn, scope){
12831         this.data.each(fn, scope);
12832     },
12833
12834     /**
12835      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12836      * (e.g., during paging).
12837      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12838      */
12839     getModifiedRecords : function(){
12840         return this.modified;
12841     },
12842
12843     // private
12844     createFilterFn : function(property, value, anyMatch){
12845         if(!value.exec){ // not a regex
12846             value = String(value);
12847             if(value.length == 0){
12848                 return false;
12849             }
12850             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12851         }
12852         return function(r){
12853             return value.test(r.data[property]);
12854         };
12855     },
12856
12857     /**
12858      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12859      * @param {String} property A field on your records
12860      * @param {Number} start The record index to start at (defaults to 0)
12861      * @param {Number} end The last record index to include (defaults to length - 1)
12862      * @return {Number} The sum
12863      */
12864     sum : function(property, start, end){
12865         var rs = this.data.items, v = 0;
12866         start = start || 0;
12867         end = (end || end === 0) ? end : rs.length-1;
12868
12869         for(var i = start; i <= end; i++){
12870             v += (rs[i].data[property] || 0);
12871         }
12872         return v;
12873     },
12874
12875     /**
12876      * Filter the records by a specified property.
12877      * @param {String} field A field on your records
12878      * @param {String/RegExp} value Either a string that the field
12879      * should start with or a RegExp to test against the field
12880      * @param {Boolean} anyMatch True to match any part not just the beginning
12881      */
12882     filter : function(property, value, anyMatch){
12883         var fn = this.createFilterFn(property, value, anyMatch);
12884         return fn ? this.filterBy(fn) : this.clearFilter();
12885     },
12886
12887     /**
12888      * Filter by a function. The specified function will be called with each
12889      * record in this data source. If the function returns true the record is included,
12890      * otherwise it is filtered.
12891      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12892      * @param {Object} scope (optional) The scope of the function (defaults to this)
12893      */
12894     filterBy : function(fn, scope){
12895         this.snapshot = this.snapshot || this.data;
12896         this.data = this.queryBy(fn, scope||this);
12897         this.fireEvent("datachanged", this);
12898     },
12899
12900     /**
12901      * Query the records by a specified property.
12902      * @param {String} field A field on your records
12903      * @param {String/RegExp} value Either a string that the field
12904      * should start with or a RegExp to test against the field
12905      * @param {Boolean} anyMatch True to match any part not just the beginning
12906      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12907      */
12908     query : function(property, value, anyMatch){
12909         var fn = this.createFilterFn(property, value, anyMatch);
12910         return fn ? this.queryBy(fn) : this.data.clone();
12911     },
12912
12913     /**
12914      * Query by a function. The specified function will be called with each
12915      * record in this data source. If the function returns true the record is included
12916      * in the results.
12917      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12918      * @param {Object} scope (optional) The scope of the function (defaults to this)
12919       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12920      **/
12921     queryBy : function(fn, scope){
12922         var data = this.snapshot || this.data;
12923         return data.filterBy(fn, scope||this);
12924     },
12925
12926     /**
12927      * Collects unique values for a particular dataIndex from this store.
12928      * @param {String} dataIndex The property to collect
12929      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12930      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12931      * @return {Array} An array of the unique values
12932      **/
12933     collect : function(dataIndex, allowNull, bypassFilter){
12934         var d = (bypassFilter === true && this.snapshot) ?
12935                 this.snapshot.items : this.data.items;
12936         var v, sv, r = [], l = {};
12937         for(var i = 0, len = d.length; i < len; i++){
12938             v = d[i].data[dataIndex];
12939             sv = String(v);
12940             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12941                 l[sv] = true;
12942                 r[r.length] = v;
12943             }
12944         }
12945         return r;
12946     },
12947
12948     /**
12949      * Revert to a view of the Record cache with no filtering applied.
12950      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12951      */
12952     clearFilter : function(suppressEvent){
12953         if(this.snapshot && this.snapshot != this.data){
12954             this.data = this.snapshot;
12955             delete this.snapshot;
12956             if(suppressEvent !== true){
12957                 this.fireEvent("datachanged", this);
12958             }
12959         }
12960     },
12961
12962     // private
12963     afterEdit : function(record){
12964         if(this.modified.indexOf(record) == -1){
12965             this.modified.push(record);
12966         }
12967         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12968     },
12969     
12970     // private
12971     afterReject : function(record){
12972         this.modified.remove(record);
12973         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12974     },
12975
12976     // private
12977     afterCommit : function(record){
12978         this.modified.remove(record);
12979         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12980     },
12981
12982     /**
12983      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12984      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12985      */
12986     commitChanges : function(){
12987         var m = this.modified.slice(0);
12988         this.modified = [];
12989         for(var i = 0, len = m.length; i < len; i++){
12990             m[i].commit();
12991         }
12992     },
12993
12994     /**
12995      * Cancel outstanding changes on all changed records.
12996      */
12997     rejectChanges : function(){
12998         var m = this.modified.slice(0);
12999         this.modified = [];
13000         for(var i = 0, len = m.length; i < len; i++){
13001             m[i].reject();
13002         }
13003     },
13004
13005     onMetaChange : function(meta, rtype, o){
13006         this.recordType = rtype;
13007         this.fields = rtype.prototype.fields;
13008         delete this.snapshot;
13009         this.sortInfo = meta.sortInfo || this.sortInfo;
13010         this.modified = [];
13011         this.fireEvent('metachange', this, this.reader.meta);
13012     },
13013     
13014     moveIndex : function(data, type)
13015     {
13016         var index = this.indexOf(data);
13017         
13018         var newIndex = index + type;
13019         
13020         this.remove(data);
13021         
13022         this.insert(newIndex, data);
13023         
13024     }
13025 });/*
13026  * Based on:
13027  * Ext JS Library 1.1.1
13028  * Copyright(c) 2006-2007, Ext JS, LLC.
13029  *
13030  * Originally Released Under LGPL - original licence link has changed is not relivant.
13031  *
13032  * Fork - LGPL
13033  * <script type="text/javascript">
13034  */
13035
13036 /**
13037  * @class Roo.data.SimpleStore
13038  * @extends Roo.data.Store
13039  * Small helper class to make creating Stores from Array data easier.
13040  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13041  * @cfg {Array} fields An array of field definition objects, or field name strings.
13042  * @cfg {Object} an existing reader (eg. copied from another store)
13043  * @cfg {Array} data The multi-dimensional array of data
13044  * @constructor
13045  * @param {Object} config
13046  */
13047 Roo.data.SimpleStore = function(config)
13048 {
13049     Roo.data.SimpleStore.superclass.constructor.call(this, {
13050         isLocal : true,
13051         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13052                 id: config.id
13053             },
13054             Roo.data.Record.create(config.fields)
13055         ),
13056         proxy : new Roo.data.MemoryProxy(config.data)
13057     });
13058     this.load();
13059 };
13060 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13061  * Based on:
13062  * Ext JS Library 1.1.1
13063  * Copyright(c) 2006-2007, Ext JS, LLC.
13064  *
13065  * Originally Released Under LGPL - original licence link has changed is not relivant.
13066  *
13067  * Fork - LGPL
13068  * <script type="text/javascript">
13069  */
13070
13071 /**
13072 /**
13073  * @extends Roo.data.Store
13074  * @class Roo.data.JsonStore
13075  * Small helper class to make creating Stores for JSON data easier. <br/>
13076 <pre><code>
13077 var store = new Roo.data.JsonStore({
13078     url: 'get-images.php',
13079     root: 'images',
13080     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13081 });
13082 </code></pre>
13083  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13084  * JsonReader and HttpProxy (unless inline data is provided).</b>
13085  * @cfg {Array} fields An array of field definition objects, or field name strings.
13086  * @constructor
13087  * @param {Object} config
13088  */
13089 Roo.data.JsonStore = function(c){
13090     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13091         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13092         reader: new Roo.data.JsonReader(c, c.fields)
13093     }));
13094 };
13095 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13096  * Based on:
13097  * Ext JS Library 1.1.1
13098  * Copyright(c) 2006-2007, Ext JS, LLC.
13099  *
13100  * Originally Released Under LGPL - original licence link has changed is not relivant.
13101  *
13102  * Fork - LGPL
13103  * <script type="text/javascript">
13104  */
13105
13106  
13107 Roo.data.Field = function(config){
13108     if(typeof config == "string"){
13109         config = {name: config};
13110     }
13111     Roo.apply(this, config);
13112     
13113     if(!this.type){
13114         this.type = "auto";
13115     }
13116     
13117     var st = Roo.data.SortTypes;
13118     // named sortTypes are supported, here we look them up
13119     if(typeof this.sortType == "string"){
13120         this.sortType = st[this.sortType];
13121     }
13122     
13123     // set default sortType for strings and dates
13124     if(!this.sortType){
13125         switch(this.type){
13126             case "string":
13127                 this.sortType = st.asUCString;
13128                 break;
13129             case "date":
13130                 this.sortType = st.asDate;
13131                 break;
13132             default:
13133                 this.sortType = st.none;
13134         }
13135     }
13136
13137     // define once
13138     var stripRe = /[\$,%]/g;
13139
13140     // prebuilt conversion function for this field, instead of
13141     // switching every time we're reading a value
13142     if(!this.convert){
13143         var cv, dateFormat = this.dateFormat;
13144         switch(this.type){
13145             case "":
13146             case "auto":
13147             case undefined:
13148                 cv = function(v){ return v; };
13149                 break;
13150             case "string":
13151                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13152                 break;
13153             case "int":
13154                 cv = function(v){
13155                     return v !== undefined && v !== null && v !== '' ?
13156                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13157                     };
13158                 break;
13159             case "float":
13160                 cv = function(v){
13161                     return v !== undefined && v !== null && v !== '' ?
13162                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13163                     };
13164                 break;
13165             case "bool":
13166             case "boolean":
13167                 cv = function(v){ return v === true || v === "true" || v == 1; };
13168                 break;
13169             case "date":
13170                 cv = function(v){
13171                     if(!v){
13172                         return '';
13173                     }
13174                     if(v instanceof Date){
13175                         return v;
13176                     }
13177                     if(dateFormat){
13178                         if(dateFormat == "timestamp"){
13179                             return new Date(v*1000);
13180                         }
13181                         return Date.parseDate(v, dateFormat);
13182                     }
13183                     var parsed = Date.parse(v);
13184                     return parsed ? new Date(parsed) : null;
13185                 };
13186              break;
13187             
13188         }
13189         this.convert = cv;
13190     }
13191 };
13192
13193 Roo.data.Field.prototype = {
13194     dateFormat: null,
13195     defaultValue: "",
13196     mapping: null,
13197     sortType : null,
13198     sortDir : "ASC"
13199 };/*
13200  * Based on:
13201  * Ext JS Library 1.1.1
13202  * Copyright(c) 2006-2007, Ext JS, LLC.
13203  *
13204  * Originally Released Under LGPL - original licence link has changed is not relivant.
13205  *
13206  * Fork - LGPL
13207  * <script type="text/javascript">
13208  */
13209  
13210 // Base class for reading structured data from a data source.  This class is intended to be
13211 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13212
13213 /**
13214  * @class Roo.data.DataReader
13215  * Base class for reading structured data from a data source.  This class is intended to be
13216  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13217  */
13218
13219 Roo.data.DataReader = function(meta, recordType){
13220     
13221     this.meta = meta;
13222     
13223     this.recordType = recordType instanceof Array ? 
13224         Roo.data.Record.create(recordType) : recordType;
13225 };
13226
13227 Roo.data.DataReader.prototype = {
13228     
13229     
13230     readerType : 'Data',
13231      /**
13232      * Create an empty record
13233      * @param {Object} data (optional) - overlay some values
13234      * @return {Roo.data.Record} record created.
13235      */
13236     newRow :  function(d) {
13237         var da =  {};
13238         this.recordType.prototype.fields.each(function(c) {
13239             switch( c.type) {
13240                 case 'int' : da[c.name] = 0; break;
13241                 case 'date' : da[c.name] = new Date(); break;
13242                 case 'float' : da[c.name] = 0.0; break;
13243                 case 'boolean' : da[c.name] = false; break;
13244                 default : da[c.name] = ""; break;
13245             }
13246             
13247         });
13248         return new this.recordType(Roo.apply(da, d));
13249     }
13250     
13251     
13252 };/*
13253  * Based on:
13254  * Ext JS Library 1.1.1
13255  * Copyright(c) 2006-2007, Ext JS, LLC.
13256  *
13257  * Originally Released Under LGPL - original licence link has changed is not relivant.
13258  *
13259  * Fork - LGPL
13260  * <script type="text/javascript">
13261  */
13262
13263 /**
13264  * @class Roo.data.DataProxy
13265  * @extends Roo.data.Observable
13266  * This class is an abstract base class for implementations which provide retrieval of
13267  * unformatted data objects.<br>
13268  * <p>
13269  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13270  * (of the appropriate type which knows how to parse the data object) to provide a block of
13271  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13272  * <p>
13273  * Custom implementations must implement the load method as described in
13274  * {@link Roo.data.HttpProxy#load}.
13275  */
13276 Roo.data.DataProxy = function(){
13277     this.addEvents({
13278         /**
13279          * @event beforeload
13280          * Fires before a network request is made to retrieve a data object.
13281          * @param {Object} This DataProxy object.
13282          * @param {Object} params The params parameter to the load function.
13283          */
13284         beforeload : true,
13285         /**
13286          * @event load
13287          * Fires before the load method's callback is called.
13288          * @param {Object} This DataProxy object.
13289          * @param {Object} o The data object.
13290          * @param {Object} arg The callback argument object passed to the load function.
13291          */
13292         load : true,
13293         /**
13294          * @event loadexception
13295          * Fires if an Exception occurs during data retrieval.
13296          * @param {Object} This DataProxy object.
13297          * @param {Object} o The data object.
13298          * @param {Object} arg The callback argument object passed to the load function.
13299          * @param {Object} e The Exception.
13300          */
13301         loadexception : true
13302     });
13303     Roo.data.DataProxy.superclass.constructor.call(this);
13304 };
13305
13306 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13307
13308     /**
13309      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13310      */
13311 /*
13312  * Based on:
13313  * Ext JS Library 1.1.1
13314  * Copyright(c) 2006-2007, Ext JS, LLC.
13315  *
13316  * Originally Released Under LGPL - original licence link has changed is not relivant.
13317  *
13318  * Fork - LGPL
13319  * <script type="text/javascript">
13320  */
13321 /**
13322  * @class Roo.data.MemoryProxy
13323  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13324  * to the Reader when its load method is called.
13325  * @constructor
13326  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13327  */
13328 Roo.data.MemoryProxy = function(data){
13329     if (data.data) {
13330         data = data.data;
13331     }
13332     Roo.data.MemoryProxy.superclass.constructor.call(this);
13333     this.data = data;
13334 };
13335
13336 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13337     
13338     /**
13339      * Load data from the requested source (in this case an in-memory
13340      * data object passed to the constructor), read the data object into
13341      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13342      * process that block using the passed callback.
13343      * @param {Object} params This parameter is not used by the MemoryProxy class.
13344      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13345      * object into a block of Roo.data.Records.
13346      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13347      * The function must be passed <ul>
13348      * <li>The Record block object</li>
13349      * <li>The "arg" argument from the load function</li>
13350      * <li>A boolean success indicator</li>
13351      * </ul>
13352      * @param {Object} scope The scope in which to call the callback
13353      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13354      */
13355     load : function(params, reader, callback, scope, arg){
13356         params = params || {};
13357         var result;
13358         try {
13359             result = reader.readRecords(params.data ? params.data :this.data);
13360         }catch(e){
13361             this.fireEvent("loadexception", this, arg, null, e);
13362             callback.call(scope, null, arg, false);
13363             return;
13364         }
13365         callback.call(scope, result, arg, true);
13366     },
13367     
13368     // private
13369     update : function(params, records){
13370         
13371     }
13372 });/*
13373  * Based on:
13374  * Ext JS Library 1.1.1
13375  * Copyright(c) 2006-2007, Ext JS, LLC.
13376  *
13377  * Originally Released Under LGPL - original licence link has changed is not relivant.
13378  *
13379  * Fork - LGPL
13380  * <script type="text/javascript">
13381  */
13382 /**
13383  * @class Roo.data.HttpProxy
13384  * @extends Roo.data.DataProxy
13385  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13386  * configured to reference a certain URL.<br><br>
13387  * <p>
13388  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13389  * from which the running page was served.<br><br>
13390  * <p>
13391  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13392  * <p>
13393  * Be aware that to enable the browser to parse an XML document, the server must set
13394  * the Content-Type header in the HTTP response to "text/xml".
13395  * @constructor
13396  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13397  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13398  * will be used to make the request.
13399  */
13400 Roo.data.HttpProxy = function(conn){
13401     Roo.data.HttpProxy.superclass.constructor.call(this);
13402     // is conn a conn config or a real conn?
13403     this.conn = conn;
13404     this.useAjax = !conn || !conn.events;
13405   
13406 };
13407
13408 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13409     // thse are take from connection...
13410     
13411     /**
13412      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13413      */
13414     /**
13415      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13416      * extra parameters to each request made by this object. (defaults to undefined)
13417      */
13418     /**
13419      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13420      *  to each request made by this object. (defaults to undefined)
13421      */
13422     /**
13423      * @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)
13424      */
13425     /**
13426      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13427      */
13428      /**
13429      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13430      * @type Boolean
13431      */
13432   
13433
13434     /**
13435      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13436      * @type Boolean
13437      */
13438     /**
13439      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13440      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13441      * a finer-grained basis than the DataProxy events.
13442      */
13443     getConnection : function(){
13444         return this.useAjax ? Roo.Ajax : this.conn;
13445     },
13446
13447     /**
13448      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13449      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13450      * process that block using the passed callback.
13451      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13452      * for the request to the remote server.
13453      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13454      * object into a block of Roo.data.Records.
13455      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13456      * The function must be passed <ul>
13457      * <li>The Record block object</li>
13458      * <li>The "arg" argument from the load function</li>
13459      * <li>A boolean success indicator</li>
13460      * </ul>
13461      * @param {Object} scope The scope in which to call the callback
13462      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13463      */
13464     load : function(params, reader, callback, scope, arg){
13465         if(this.fireEvent("beforeload", this, params) !== false){
13466             var  o = {
13467                 params : params || {},
13468                 request: {
13469                     callback : callback,
13470                     scope : scope,
13471                     arg : arg
13472                 },
13473                 reader: reader,
13474                 callback : this.loadResponse,
13475                 scope: this
13476             };
13477             if(this.useAjax){
13478                 Roo.applyIf(o, this.conn);
13479                 if(this.activeRequest){
13480                     Roo.Ajax.abort(this.activeRequest);
13481                 }
13482                 this.activeRequest = Roo.Ajax.request(o);
13483             }else{
13484                 this.conn.request(o);
13485             }
13486         }else{
13487             callback.call(scope||this, null, arg, false);
13488         }
13489     },
13490
13491     // private
13492     loadResponse : function(o, success, response){
13493         delete this.activeRequest;
13494         if(!success){
13495             this.fireEvent("loadexception", this, o, response);
13496             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13497             return;
13498         }
13499         var result;
13500         try {
13501             result = o.reader.read(response);
13502         }catch(e){
13503             this.fireEvent("loadexception", this, o, response, e);
13504             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13505             return;
13506         }
13507         
13508         this.fireEvent("load", this, o, o.request.arg);
13509         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13510     },
13511
13512     // private
13513     update : function(dataSet){
13514
13515     },
13516
13517     // private
13518     updateResponse : function(dataSet){
13519
13520     }
13521 });/*
13522  * Based on:
13523  * Ext JS Library 1.1.1
13524  * Copyright(c) 2006-2007, Ext JS, LLC.
13525  *
13526  * Originally Released Under LGPL - original licence link has changed is not relivant.
13527  *
13528  * Fork - LGPL
13529  * <script type="text/javascript">
13530  */
13531
13532 /**
13533  * @class Roo.data.ScriptTagProxy
13534  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13535  * other than the originating domain of the running page.<br><br>
13536  * <p>
13537  * <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
13538  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13539  * <p>
13540  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13541  * source code that is used as the source inside a &lt;script> tag.<br><br>
13542  * <p>
13543  * In order for the browser to process the returned data, the server must wrap the data object
13544  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13545  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13546  * depending on whether the callback name was passed:
13547  * <p>
13548  * <pre><code>
13549 boolean scriptTag = false;
13550 String cb = request.getParameter("callback");
13551 if (cb != null) {
13552     scriptTag = true;
13553     response.setContentType("text/javascript");
13554 } else {
13555     response.setContentType("application/x-json");
13556 }
13557 Writer out = response.getWriter();
13558 if (scriptTag) {
13559     out.write(cb + "(");
13560 }
13561 out.print(dataBlock.toJsonString());
13562 if (scriptTag) {
13563     out.write(");");
13564 }
13565 </pre></code>
13566  *
13567  * @constructor
13568  * @param {Object} config A configuration object.
13569  */
13570 Roo.data.ScriptTagProxy = function(config){
13571     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13572     Roo.apply(this, config);
13573     this.head = document.getElementsByTagName("head")[0];
13574 };
13575
13576 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13577
13578 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13579     /**
13580      * @cfg {String} url The URL from which to request the data object.
13581      */
13582     /**
13583      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13584      */
13585     timeout : 30000,
13586     /**
13587      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13588      * the server the name of the callback function set up by the load call to process the returned data object.
13589      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13590      * javascript output which calls this named function passing the data object as its only parameter.
13591      */
13592     callbackParam : "callback",
13593     /**
13594      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13595      * name to the request.
13596      */
13597     nocache : true,
13598
13599     /**
13600      * Load data from the configured URL, read the data object into
13601      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13602      * process that block using the passed callback.
13603      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13604      * for the request to the remote server.
13605      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13606      * object into a block of Roo.data.Records.
13607      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13608      * The function must be passed <ul>
13609      * <li>The Record block object</li>
13610      * <li>The "arg" argument from the load function</li>
13611      * <li>A boolean success indicator</li>
13612      * </ul>
13613      * @param {Object} scope The scope in which to call the callback
13614      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13615      */
13616     load : function(params, reader, callback, scope, arg){
13617         if(this.fireEvent("beforeload", this, params) !== false){
13618
13619             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13620
13621             var url = this.url;
13622             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13623             if(this.nocache){
13624                 url += "&_dc=" + (new Date().getTime());
13625             }
13626             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13627             var trans = {
13628                 id : transId,
13629                 cb : "stcCallback"+transId,
13630                 scriptId : "stcScript"+transId,
13631                 params : params,
13632                 arg : arg,
13633                 url : url,
13634                 callback : callback,
13635                 scope : scope,
13636                 reader : reader
13637             };
13638             var conn = this;
13639
13640             window[trans.cb] = function(o){
13641                 conn.handleResponse(o, trans);
13642             };
13643
13644             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13645
13646             if(this.autoAbort !== false){
13647                 this.abort();
13648             }
13649
13650             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13651
13652             var script = document.createElement("script");
13653             script.setAttribute("src", url);
13654             script.setAttribute("type", "text/javascript");
13655             script.setAttribute("id", trans.scriptId);
13656             this.head.appendChild(script);
13657
13658             this.trans = trans;
13659         }else{
13660             callback.call(scope||this, null, arg, false);
13661         }
13662     },
13663
13664     // private
13665     isLoading : function(){
13666         return this.trans ? true : false;
13667     },
13668
13669     /**
13670      * Abort the current server request.
13671      */
13672     abort : function(){
13673         if(this.isLoading()){
13674             this.destroyTrans(this.trans);
13675         }
13676     },
13677
13678     // private
13679     destroyTrans : function(trans, isLoaded){
13680         this.head.removeChild(document.getElementById(trans.scriptId));
13681         clearTimeout(trans.timeoutId);
13682         if(isLoaded){
13683             window[trans.cb] = undefined;
13684             try{
13685                 delete window[trans.cb];
13686             }catch(e){}
13687         }else{
13688             // if hasn't been loaded, wait for load to remove it to prevent script error
13689             window[trans.cb] = function(){
13690                 window[trans.cb] = undefined;
13691                 try{
13692                     delete window[trans.cb];
13693                 }catch(e){}
13694             };
13695         }
13696     },
13697
13698     // private
13699     handleResponse : function(o, trans){
13700         this.trans = false;
13701         this.destroyTrans(trans, true);
13702         var result;
13703         try {
13704             result = trans.reader.readRecords(o);
13705         }catch(e){
13706             this.fireEvent("loadexception", this, o, trans.arg, e);
13707             trans.callback.call(trans.scope||window, null, trans.arg, false);
13708             return;
13709         }
13710         this.fireEvent("load", this, o, trans.arg);
13711         trans.callback.call(trans.scope||window, result, trans.arg, true);
13712     },
13713
13714     // private
13715     handleFailure : function(trans){
13716         this.trans = false;
13717         this.destroyTrans(trans, false);
13718         this.fireEvent("loadexception", this, null, trans.arg);
13719         trans.callback.call(trans.scope||window, null, trans.arg, false);
13720     }
13721 });/*
13722  * Based on:
13723  * Ext JS Library 1.1.1
13724  * Copyright(c) 2006-2007, Ext JS, LLC.
13725  *
13726  * Originally Released Under LGPL - original licence link has changed is not relivant.
13727  *
13728  * Fork - LGPL
13729  * <script type="text/javascript">
13730  */
13731
13732 /**
13733  * @class Roo.data.JsonReader
13734  * @extends Roo.data.DataReader
13735  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13736  * based on mappings in a provided Roo.data.Record constructor.
13737  * 
13738  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13739  * in the reply previously. 
13740  * 
13741  * <p>
13742  * Example code:
13743  * <pre><code>
13744 var RecordDef = Roo.data.Record.create([
13745     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13746     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13747 ]);
13748 var myReader = new Roo.data.JsonReader({
13749     totalProperty: "results",    // The property which contains the total dataset size (optional)
13750     root: "rows",                // The property which contains an Array of row objects
13751     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13752 }, RecordDef);
13753 </code></pre>
13754  * <p>
13755  * This would consume a JSON file like this:
13756  * <pre><code>
13757 { 'results': 2, 'rows': [
13758     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13759     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13760 }
13761 </code></pre>
13762  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13763  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13764  * paged from the remote server.
13765  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13766  * @cfg {String} root name of the property which contains the Array of row objects.
13767  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13768  * @cfg {Array} fields Array of field definition objects
13769  * @constructor
13770  * Create a new JsonReader
13771  * @param {Object} meta Metadata configuration options
13772  * @param {Object} recordType Either an Array of field definition objects,
13773  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13774  */
13775 Roo.data.JsonReader = function(meta, recordType){
13776     
13777     meta = meta || {};
13778     // set some defaults:
13779     Roo.applyIf(meta, {
13780         totalProperty: 'total',
13781         successProperty : 'success',
13782         root : 'data',
13783         id : 'id'
13784     });
13785     
13786     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13787 };
13788 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13789     
13790     readerType : 'Json',
13791     
13792     /**
13793      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13794      * Used by Store query builder to append _requestMeta to params.
13795      * 
13796      */
13797     metaFromRemote : false,
13798     /**
13799      * This method is only used by a DataProxy which has retrieved data from a remote server.
13800      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13801      * @return {Object} data A data block which is used by an Roo.data.Store object as
13802      * a cache of Roo.data.Records.
13803      */
13804     read : function(response){
13805         var json = response.responseText;
13806        
13807         var o = /* eval:var:o */ eval("("+json+")");
13808         if(!o) {
13809             throw {message: "JsonReader.read: Json object not found"};
13810         }
13811         
13812         if(o.metaData){
13813             
13814             delete this.ef;
13815             this.metaFromRemote = true;
13816             this.meta = o.metaData;
13817             this.recordType = Roo.data.Record.create(o.metaData.fields);
13818             this.onMetaChange(this.meta, this.recordType, o);
13819         }
13820         return this.readRecords(o);
13821     },
13822
13823     // private function a store will implement
13824     onMetaChange : function(meta, recordType, o){
13825
13826     },
13827
13828     /**
13829          * @ignore
13830          */
13831     simpleAccess: function(obj, subsc) {
13832         return obj[subsc];
13833     },
13834
13835         /**
13836          * @ignore
13837          */
13838     getJsonAccessor: function(){
13839         var re = /[\[\.]/;
13840         return function(expr) {
13841             try {
13842                 return(re.test(expr))
13843                     ? new Function("obj", "return obj." + expr)
13844                     : function(obj){
13845                         return obj[expr];
13846                     };
13847             } catch(e){}
13848             return Roo.emptyFn;
13849         };
13850     }(),
13851
13852     /**
13853      * Create a data block containing Roo.data.Records from an XML document.
13854      * @param {Object} o An object which contains an Array of row objects in the property specified
13855      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13856      * which contains the total size of the dataset.
13857      * @return {Object} data A data block which is used by an Roo.data.Store object as
13858      * a cache of Roo.data.Records.
13859      */
13860     readRecords : function(o){
13861         /**
13862          * After any data loads, the raw JSON data is available for further custom processing.
13863          * @type Object
13864          */
13865         this.o = o;
13866         var s = this.meta, Record = this.recordType,
13867             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13868
13869 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13870         if (!this.ef) {
13871             if(s.totalProperty) {
13872                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13873                 }
13874                 if(s.successProperty) {
13875                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13876                 }
13877                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13878                 if (s.id) {
13879                         var g = this.getJsonAccessor(s.id);
13880                         this.getId = function(rec) {
13881                                 var r = g(rec);  
13882                                 return (r === undefined || r === "") ? null : r;
13883                         };
13884                 } else {
13885                         this.getId = function(){return null;};
13886                 }
13887             this.ef = [];
13888             for(var jj = 0; jj < fl; jj++){
13889                 f = fi[jj];
13890                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13891                 this.ef[jj] = this.getJsonAccessor(map);
13892             }
13893         }
13894
13895         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13896         if(s.totalProperty){
13897             var vt = parseInt(this.getTotal(o), 10);
13898             if(!isNaN(vt)){
13899                 totalRecords = vt;
13900             }
13901         }
13902         if(s.successProperty){
13903             var vs = this.getSuccess(o);
13904             if(vs === false || vs === 'false'){
13905                 success = false;
13906             }
13907         }
13908         var records = [];
13909         for(var i = 0; i < c; i++){
13910                 var n = root[i];
13911             var values = {};
13912             var id = this.getId(n);
13913             for(var j = 0; j < fl; j++){
13914                 f = fi[j];
13915             var v = this.ef[j](n);
13916             if (!f.convert) {
13917                 Roo.log('missing convert for ' + f.name);
13918                 Roo.log(f);
13919                 continue;
13920             }
13921             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13922             }
13923             var record = new Record(values, id);
13924             record.json = n;
13925             records[i] = record;
13926         }
13927         return {
13928             raw : o,
13929             success : success,
13930             records : records,
13931             totalRecords : totalRecords
13932         };
13933     },
13934     // used when loading children.. @see loadDataFromChildren
13935     toLoadData: function(rec)
13936     {
13937         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13938         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13939         return { data : data, total : data.length };
13940         
13941     }
13942 });/*
13943  * Based on:
13944  * Ext JS Library 1.1.1
13945  * Copyright(c) 2006-2007, Ext JS, LLC.
13946  *
13947  * Originally Released Under LGPL - original licence link has changed is not relivant.
13948  *
13949  * Fork - LGPL
13950  * <script type="text/javascript">
13951  */
13952
13953 /**
13954  * @class Roo.data.ArrayReader
13955  * @extends Roo.data.DataReader
13956  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13957  * Each element of that Array represents a row of data fields. The
13958  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13959  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13960  * <p>
13961  * Example code:.
13962  * <pre><code>
13963 var RecordDef = Roo.data.Record.create([
13964     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13965     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13966 ]);
13967 var myReader = new Roo.data.ArrayReader({
13968     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13969 }, RecordDef);
13970 </code></pre>
13971  * <p>
13972  * This would consume an Array like this:
13973  * <pre><code>
13974 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13975   </code></pre>
13976  
13977  * @constructor
13978  * Create a new JsonReader
13979  * @param {Object} meta Metadata configuration options.
13980  * @param {Object|Array} recordType Either an Array of field definition objects
13981  * 
13982  * @cfg {Array} fields Array of field definition objects
13983  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13984  * as specified to {@link Roo.data.Record#create},
13985  * or an {@link Roo.data.Record} object
13986  *
13987  * 
13988  * created using {@link Roo.data.Record#create}.
13989  */
13990 Roo.data.ArrayReader = function(meta, recordType)
13991 {    
13992     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13993 };
13994
13995 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13996     
13997       /**
13998      * Create a data block containing Roo.data.Records from an XML document.
13999      * @param {Object} o An Array of row objects which represents the dataset.
14000      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14001      * a cache of Roo.data.Records.
14002      */
14003     readRecords : function(o)
14004     {
14005         var sid = this.meta ? this.meta.id : null;
14006         var recordType = this.recordType, fields = recordType.prototype.fields;
14007         var records = [];
14008         var root = o;
14009         for(var i = 0; i < root.length; i++){
14010                 var n = root[i];
14011             var values = {};
14012             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14013             for(var j = 0, jlen = fields.length; j < jlen; j++){
14014                 var f = fields.items[j];
14015                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14016                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14017                 v = f.convert(v);
14018                 values[f.name] = v;
14019             }
14020             var record = new recordType(values, id);
14021             record.json = n;
14022             records[records.length] = record;
14023         }
14024         return {
14025             records : records,
14026             totalRecords : records.length
14027         };
14028     },
14029     // used when loading children.. @see loadDataFromChildren
14030     toLoadData: function(rec)
14031     {
14032         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14033         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14034         
14035     }
14036     
14037     
14038 });/*
14039  * - LGPL
14040  * * 
14041  */
14042
14043 /**
14044  * @class Roo.bootstrap.ComboBox
14045  * @extends Roo.bootstrap.TriggerField
14046  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14047  * @cfg {Boolean} append (true|false) default false
14048  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14049  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14050  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14051  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14052  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14053  * @cfg {Boolean} animate default true
14054  * @cfg {Boolean} emptyResultText only for touch device
14055  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14056  * @cfg {String} emptyTitle default ''
14057  * @constructor
14058  * Create a new ComboBox.
14059  * @param {Object} config Configuration options
14060  */
14061 Roo.bootstrap.ComboBox = function(config){
14062     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14063     this.addEvents({
14064         /**
14065          * @event expand
14066          * Fires when the dropdown list is expanded
14067         * @param {Roo.bootstrap.ComboBox} combo This combo box
14068         */
14069         'expand' : true,
14070         /**
14071          * @event collapse
14072          * Fires when the dropdown list is collapsed
14073         * @param {Roo.bootstrap.ComboBox} combo This combo box
14074         */
14075         'collapse' : true,
14076         /**
14077          * @event beforeselect
14078          * Fires before a list item is selected. Return false to cancel the selection.
14079         * @param {Roo.bootstrap.ComboBox} combo This combo box
14080         * @param {Roo.data.Record} record The data record returned from the underlying store
14081         * @param {Number} index The index of the selected item in the dropdown list
14082         */
14083         'beforeselect' : true,
14084         /**
14085          * @event select
14086          * Fires when a list item is selected
14087         * @param {Roo.bootstrap.ComboBox} combo This combo box
14088         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14089         * @param {Number} index The index of the selected item in the dropdown list
14090         */
14091         'select' : true,
14092         /**
14093          * @event beforequery
14094          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14095          * The event object passed has these properties:
14096         * @param {Roo.bootstrap.ComboBox} combo This combo box
14097         * @param {String} query The query
14098         * @param {Boolean} forceAll true to force "all" query
14099         * @param {Boolean} cancel true to cancel the query
14100         * @param {Object} e The query event object
14101         */
14102         'beforequery': true,
14103          /**
14104          * @event add
14105          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14106         * @param {Roo.bootstrap.ComboBox} combo This combo box
14107         */
14108         'add' : true,
14109         /**
14110          * @event edit
14111          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14112         * @param {Roo.bootstrap.ComboBox} combo This combo box
14113         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14114         */
14115         'edit' : true,
14116         /**
14117          * @event remove
14118          * Fires when the remove value from the combobox array
14119         * @param {Roo.bootstrap.ComboBox} combo This combo box
14120         */
14121         'remove' : true,
14122         /**
14123          * @event afterremove
14124          * Fires when the remove value from the combobox array
14125         * @param {Roo.bootstrap.ComboBox} combo This combo box
14126         */
14127         'afterremove' : true,
14128         /**
14129          * @event specialfilter
14130          * Fires when specialfilter
14131             * @param {Roo.bootstrap.ComboBox} combo This combo box
14132             */
14133         'specialfilter' : true,
14134         /**
14135          * @event tick
14136          * Fires when tick the element
14137             * @param {Roo.bootstrap.ComboBox} combo This combo box
14138             */
14139         'tick' : true,
14140         /**
14141          * @event touchviewdisplay
14142          * Fires when touch view require special display (default is using displayField)
14143             * @param {Roo.bootstrap.ComboBox} combo This combo box
14144             * @param {Object} cfg set html .
14145             */
14146         'touchviewdisplay' : true
14147         
14148     });
14149     
14150     this.item = [];
14151     this.tickItems = [];
14152     
14153     this.selectedIndex = -1;
14154     if(this.mode == 'local'){
14155         if(config.queryDelay === undefined){
14156             this.queryDelay = 10;
14157         }
14158         if(config.minChars === undefined){
14159             this.minChars = 0;
14160         }
14161     }
14162 };
14163
14164 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14165      
14166     /**
14167      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14168      * rendering into an Roo.Editor, defaults to false)
14169      */
14170     /**
14171      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14172      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14173      */
14174     /**
14175      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14176      */
14177     /**
14178      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14179      * the dropdown list (defaults to undefined, with no header element)
14180      */
14181
14182      /**
14183      * @cfg {String/Roo.Template} tpl The template to use to render the output
14184      */
14185      
14186      /**
14187      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14188      */
14189     listWidth: undefined,
14190     /**
14191      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14192      * mode = 'remote' or 'text' if mode = 'local')
14193      */
14194     displayField: undefined,
14195     
14196     /**
14197      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14198      * mode = 'remote' or 'value' if mode = 'local'). 
14199      * Note: use of a valueField requires the user make a selection
14200      * in order for a value to be mapped.
14201      */
14202     valueField: undefined,
14203     /**
14204      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14205      */
14206     modalTitle : '',
14207     
14208     /**
14209      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14210      * field's data value (defaults to the underlying DOM element's name)
14211      */
14212     hiddenName: undefined,
14213     /**
14214      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14215      */
14216     listClass: '',
14217     /**
14218      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14219      */
14220     selectedClass: 'active',
14221     
14222     /**
14223      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14224      */
14225     shadow:'sides',
14226     /**
14227      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14228      * anchor positions (defaults to 'tl-bl')
14229      */
14230     listAlign: 'tl-bl?',
14231     /**
14232      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14233      */
14234     maxHeight: 300,
14235     /**
14236      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14237      * query specified by the allQuery config option (defaults to 'query')
14238      */
14239     triggerAction: 'query',
14240     /**
14241      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14242      * (defaults to 4, does not apply if editable = false)
14243      */
14244     minChars : 4,
14245     /**
14246      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14247      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14248      */
14249     typeAhead: false,
14250     /**
14251      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14252      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14253      */
14254     queryDelay: 500,
14255     /**
14256      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14257      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14258      */
14259     pageSize: 0,
14260     /**
14261      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14262      * when editable = true (defaults to false)
14263      */
14264     selectOnFocus:false,
14265     /**
14266      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14267      */
14268     queryParam: 'query',
14269     /**
14270      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14271      * when mode = 'remote' (defaults to 'Loading...')
14272      */
14273     loadingText: 'Loading...',
14274     /**
14275      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14276      */
14277     resizable: false,
14278     /**
14279      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14280      */
14281     handleHeight : 8,
14282     /**
14283      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14284      * traditional select (defaults to true)
14285      */
14286     editable: true,
14287     /**
14288      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14289      */
14290     allQuery: '',
14291     /**
14292      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14293      */
14294     mode: 'remote',
14295     /**
14296      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14297      * listWidth has a higher value)
14298      */
14299     minListWidth : 70,
14300     /**
14301      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14302      * allow the user to set arbitrary text into the field (defaults to false)
14303      */
14304     forceSelection:false,
14305     /**
14306      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14307      * if typeAhead = true (defaults to 250)
14308      */
14309     typeAheadDelay : 250,
14310     /**
14311      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14312      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14313      */
14314     valueNotFoundText : undefined,
14315     /**
14316      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14317      */
14318     blockFocus : false,
14319     
14320     /**
14321      * @cfg {Boolean} disableClear Disable showing of clear button.
14322      */
14323     disableClear : false,
14324     /**
14325      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14326      */
14327     alwaysQuery : false,
14328     
14329     /**
14330      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14331      */
14332     multiple : false,
14333     
14334     /**
14335      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14336      */
14337     invalidClass : "has-warning",
14338     
14339     /**
14340      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14341      */
14342     validClass : "has-success",
14343     
14344     /**
14345      * @cfg {Boolean} specialFilter (true|false) special filter default false
14346      */
14347     specialFilter : false,
14348     
14349     /**
14350      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14351      */
14352     mobileTouchView : true,
14353     
14354     /**
14355      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14356      */
14357     useNativeIOS : false,
14358     
14359     /**
14360      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14361      */
14362     mobile_restrict_height : false,
14363     
14364     ios_options : false,
14365     
14366     //private
14367     addicon : false,
14368     editicon: false,
14369     
14370     page: 0,
14371     hasQuery: false,
14372     append: false,
14373     loadNext: false,
14374     autoFocus : true,
14375     tickable : false,
14376     btnPosition : 'right',
14377     triggerList : true,
14378     showToggleBtn : true,
14379     animate : true,
14380     emptyResultText: 'Empty',
14381     triggerText : 'Select',
14382     emptyTitle : '',
14383     
14384     // element that contains real text value.. (when hidden is used..)
14385     
14386     getAutoCreate : function()
14387     {   
14388         var cfg = false;
14389         //render
14390         /*
14391          * Render classic select for iso
14392          */
14393         
14394         if(Roo.isIOS && this.useNativeIOS){
14395             cfg = this.getAutoCreateNativeIOS();
14396             return cfg;
14397         }
14398         
14399         /*
14400          * Touch Devices
14401          */
14402         
14403         if(Roo.isTouch && this.mobileTouchView){
14404             cfg = this.getAutoCreateTouchView();
14405             return cfg;;
14406         }
14407         
14408         /*
14409          *  Normal ComboBox
14410          */
14411         if(!this.tickable){
14412             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14413             return cfg;
14414         }
14415         
14416         /*
14417          *  ComboBox with tickable selections
14418          */
14419              
14420         var align = this.labelAlign || this.parentLabelAlign();
14421         
14422         cfg = {
14423             cls : 'form-group roo-combobox-tickable' //input-group
14424         };
14425         
14426         var btn_text_select = '';
14427         var btn_text_done = '';
14428         var btn_text_cancel = '';
14429         
14430         if (this.btn_text_show) {
14431             btn_text_select = 'Select';
14432             btn_text_done = 'Done';
14433             btn_text_cancel = 'Cancel'; 
14434         }
14435         
14436         var buttons = {
14437             tag : 'div',
14438             cls : 'tickable-buttons',
14439             cn : [
14440                 {
14441                     tag : 'button',
14442                     type : 'button',
14443                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14444                     //html : this.triggerText
14445                     html: btn_text_select
14446                 },
14447                 {
14448                     tag : 'button',
14449                     type : 'button',
14450                     name : 'ok',
14451                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14452                     //html : 'Done'
14453                     html: btn_text_done
14454                 },
14455                 {
14456                     tag : 'button',
14457                     type : 'button',
14458                     name : 'cancel',
14459                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14460                     //html : 'Cancel'
14461                     html: btn_text_cancel
14462                 }
14463             ]
14464         };
14465         
14466         if(this.editable){
14467             buttons.cn.unshift({
14468                 tag: 'input',
14469                 cls: 'roo-select2-search-field-input'
14470             });
14471         }
14472         
14473         var _this = this;
14474         
14475         Roo.each(buttons.cn, function(c){
14476             if (_this.size) {
14477                 c.cls += ' btn-' + _this.size;
14478             }
14479
14480             if (_this.disabled) {
14481                 c.disabled = true;
14482             }
14483         });
14484         
14485         var box = {
14486             tag: 'div',
14487             style : 'display: contents',
14488             cn: [
14489                 {
14490                     tag: 'input',
14491                     type : 'hidden',
14492                     cls: 'form-hidden-field'
14493                 },
14494                 {
14495                     tag: 'ul',
14496                     cls: 'roo-select2-choices',
14497                     cn:[
14498                         {
14499                             tag: 'li',
14500                             cls: 'roo-select2-search-field',
14501                             cn: [
14502                                 buttons
14503                             ]
14504                         }
14505                     ]
14506                 }
14507             ]
14508         };
14509         
14510         var combobox = {
14511             cls: 'roo-select2-container input-group roo-select2-container-multi',
14512             cn: [
14513                 
14514                 box
14515 //                {
14516 //                    tag: 'ul',
14517 //                    cls: 'typeahead typeahead-long dropdown-menu',
14518 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14519 //                }
14520             ]
14521         };
14522         
14523         if(this.hasFeedback && !this.allowBlank){
14524             
14525             var feedback = {
14526                 tag: 'span',
14527                 cls: 'glyphicon form-control-feedback'
14528             };
14529
14530             combobox.cn.push(feedback);
14531         }
14532         
14533         var indicator = {
14534             tag : 'i',
14535             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14536             tooltip : 'This field is required'
14537         };
14538         if (Roo.bootstrap.version == 4) {
14539             indicator = {
14540                 tag : 'i',
14541                 style : 'display:none'
14542             };
14543         }
14544         if (align ==='left' && this.fieldLabel.length) {
14545             
14546             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14547             
14548             cfg.cn = [
14549                 indicator,
14550                 {
14551                     tag: 'label',
14552                     'for' :  id,
14553                     cls : 'control-label col-form-label',
14554                     html : this.fieldLabel
14555
14556                 },
14557                 {
14558                     cls : "", 
14559                     cn: [
14560                         combobox
14561                     ]
14562                 }
14563
14564             ];
14565             
14566             var labelCfg = cfg.cn[1];
14567             var contentCfg = cfg.cn[2];
14568             
14569
14570             if(this.indicatorpos == 'right'){
14571                 
14572                 cfg.cn = [
14573                     {
14574                         tag: 'label',
14575                         'for' :  id,
14576                         cls : 'control-label col-form-label',
14577                         cn : [
14578                             {
14579                                 tag : 'span',
14580                                 html : this.fieldLabel
14581                             },
14582                             indicator
14583                         ]
14584                     },
14585                     {
14586                         cls : "",
14587                         cn: [
14588                             combobox
14589                         ]
14590                     }
14591
14592                 ];
14593                 
14594                 
14595                 
14596                 labelCfg = cfg.cn[0];
14597                 contentCfg = cfg.cn[1];
14598             
14599             }
14600             
14601             if(this.labelWidth > 12){
14602                 labelCfg.style = "width: " + this.labelWidth + 'px';
14603             }
14604             
14605             if(this.labelWidth < 13 && this.labelmd == 0){
14606                 this.labelmd = this.labelWidth;
14607             }
14608             
14609             if(this.labellg > 0){
14610                 labelCfg.cls += ' col-lg-' + this.labellg;
14611                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14612             }
14613             
14614             if(this.labelmd > 0){
14615                 labelCfg.cls += ' col-md-' + this.labelmd;
14616                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14617             }
14618             
14619             if(this.labelsm > 0){
14620                 labelCfg.cls += ' col-sm-' + this.labelsm;
14621                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14622             }
14623             
14624             if(this.labelxs > 0){
14625                 labelCfg.cls += ' col-xs-' + this.labelxs;
14626                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14627             }
14628                 
14629                 
14630         } else if ( this.fieldLabel.length) {
14631 //                Roo.log(" label");
14632                  cfg.cn = [
14633                    indicator,
14634                     {
14635                         tag: 'label',
14636                         //cls : 'input-group-addon',
14637                         html : this.fieldLabel
14638                     },
14639                     combobox
14640                 ];
14641                 
14642                 if(this.indicatorpos == 'right'){
14643                     cfg.cn = [
14644                         {
14645                             tag: 'label',
14646                             //cls : 'input-group-addon',
14647                             html : this.fieldLabel
14648                         },
14649                         indicator,
14650                         combobox
14651                     ];
14652                     
14653                 }
14654
14655         } else {
14656             
14657 //                Roo.log(" no label && no align");
14658                 cfg = combobox
14659                      
14660                 
14661         }
14662          
14663         var settings=this;
14664         ['xs','sm','md','lg'].map(function(size){
14665             if (settings[size]) {
14666                 cfg.cls += ' col-' + size + '-' + settings[size];
14667             }
14668         });
14669         
14670         return cfg;
14671         
14672     },
14673     
14674     _initEventsCalled : false,
14675     
14676     // private
14677     initEvents: function()
14678     {   
14679         if (this._initEventsCalled) { // as we call render... prevent looping...
14680             return;
14681         }
14682         this._initEventsCalled = true;
14683         
14684         if (!this.store) {
14685             throw "can not find store for combo";
14686         }
14687         
14688         this.indicator = this.indicatorEl();
14689         
14690         this.store = Roo.factory(this.store, Roo.data);
14691         this.store.parent = this;
14692         
14693         // if we are building from html. then this element is so complex, that we can not really
14694         // use the rendered HTML.
14695         // so we have to trash and replace the previous code.
14696         if (Roo.XComponent.build_from_html) {
14697             // remove this element....
14698             var e = this.el.dom, k=0;
14699             while (e ) { e = e.previousSibling;  ++k;}
14700
14701             this.el.remove();
14702             
14703             this.el=false;
14704             this.rendered = false;
14705             
14706             this.render(this.parent().getChildContainer(true), k);
14707         }
14708         
14709         if(Roo.isIOS && this.useNativeIOS){
14710             this.initIOSView();
14711             return;
14712         }
14713         
14714         /*
14715          * Touch Devices
14716          */
14717         
14718         if(Roo.isTouch && this.mobileTouchView){
14719             this.initTouchView();
14720             return;
14721         }
14722         
14723         if(this.tickable){
14724             this.initTickableEvents();
14725             return;
14726         }
14727         
14728         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14729         
14730         if(this.hiddenName){
14731             
14732             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14733             
14734             this.hiddenField.dom.value =
14735                 this.hiddenValue !== undefined ? this.hiddenValue :
14736                 this.value !== undefined ? this.value : '';
14737
14738             // prevent input submission
14739             this.el.dom.removeAttribute('name');
14740             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14741              
14742              
14743         }
14744         //if(Roo.isGecko){
14745         //    this.el.dom.setAttribute('autocomplete', 'off');
14746         //}
14747         
14748         var cls = 'x-combo-list';
14749         
14750         //this.list = new Roo.Layer({
14751         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14752         //});
14753         
14754         var _this = this;
14755         
14756         (function(){
14757             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14758             _this.list.setWidth(lw);
14759         }).defer(100);
14760         
14761         this.list.on('mouseover', this.onViewOver, this);
14762         this.list.on('mousemove', this.onViewMove, this);
14763         this.list.on('scroll', this.onViewScroll, this);
14764         
14765         /*
14766         this.list.swallowEvent('mousewheel');
14767         this.assetHeight = 0;
14768
14769         if(this.title){
14770             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14771             this.assetHeight += this.header.getHeight();
14772         }
14773
14774         this.innerList = this.list.createChild({cls:cls+'-inner'});
14775         this.innerList.on('mouseover', this.onViewOver, this);
14776         this.innerList.on('mousemove', this.onViewMove, this);
14777         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14778         
14779         if(this.allowBlank && !this.pageSize && !this.disableClear){
14780             this.footer = this.list.createChild({cls:cls+'-ft'});
14781             this.pageTb = new Roo.Toolbar(this.footer);
14782            
14783         }
14784         if(this.pageSize){
14785             this.footer = this.list.createChild({cls:cls+'-ft'});
14786             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14787                     {pageSize: this.pageSize});
14788             
14789         }
14790         
14791         if (this.pageTb && this.allowBlank && !this.disableClear) {
14792             var _this = this;
14793             this.pageTb.add(new Roo.Toolbar.Fill(), {
14794                 cls: 'x-btn-icon x-btn-clear',
14795                 text: '&#160;',
14796                 handler: function()
14797                 {
14798                     _this.collapse();
14799                     _this.clearValue();
14800                     _this.onSelect(false, -1);
14801                 }
14802             });
14803         }
14804         if (this.footer) {
14805             this.assetHeight += this.footer.getHeight();
14806         }
14807         */
14808             
14809         if(!this.tpl){
14810             this.tpl = Roo.bootstrap.version == 4 ?
14811                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14812                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14813         }
14814
14815         this.view = new Roo.View(this.list, this.tpl, {
14816             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14817         });
14818         //this.view.wrapEl.setDisplayed(false);
14819         this.view.on('click', this.onViewClick, this);
14820         
14821         
14822         this.store.on('beforeload', this.onBeforeLoad, this);
14823         this.store.on('load', this.onLoad, this);
14824         this.store.on('loadexception', this.onLoadException, this);
14825         /*
14826         if(this.resizable){
14827             this.resizer = new Roo.Resizable(this.list,  {
14828                pinned:true, handles:'se'
14829             });
14830             this.resizer.on('resize', function(r, w, h){
14831                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14832                 this.listWidth = w;
14833                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14834                 this.restrictHeight();
14835             }, this);
14836             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14837         }
14838         */
14839         if(!this.editable){
14840             this.editable = true;
14841             this.setEditable(false);
14842         }
14843         
14844         /*
14845         
14846         if (typeof(this.events.add.listeners) != 'undefined') {
14847             
14848             this.addicon = this.wrap.createChild(
14849                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14850        
14851             this.addicon.on('click', function(e) {
14852                 this.fireEvent('add', this);
14853             }, this);
14854         }
14855         if (typeof(this.events.edit.listeners) != 'undefined') {
14856             
14857             this.editicon = this.wrap.createChild(
14858                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14859             if (this.addicon) {
14860                 this.editicon.setStyle('margin-left', '40px');
14861             }
14862             this.editicon.on('click', function(e) {
14863                 
14864                 // we fire even  if inothing is selected..
14865                 this.fireEvent('edit', this, this.lastData );
14866                 
14867             }, this);
14868         }
14869         */
14870         
14871         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14872             "up" : function(e){
14873                 this.inKeyMode = true;
14874                 this.selectPrev();
14875             },
14876
14877             "down" : function(e){
14878                 if(!this.isExpanded()){
14879                     this.onTriggerClick();
14880                 }else{
14881                     this.inKeyMode = true;
14882                     this.selectNext();
14883                 }
14884             },
14885
14886             "enter" : function(e){
14887 //                this.onViewClick();
14888                 //return true;
14889                 this.collapse();
14890                 
14891                 if(this.fireEvent("specialkey", this, e)){
14892                     this.onViewClick(false);
14893                 }
14894                 
14895                 return true;
14896             },
14897
14898             "esc" : function(e){
14899                 this.collapse();
14900             },
14901
14902             "tab" : function(e){
14903                 this.collapse();
14904                 
14905                 if(this.fireEvent("specialkey", this, e)){
14906                     this.onViewClick(false);
14907                 }
14908                 
14909                 return true;
14910             },
14911
14912             scope : this,
14913
14914             doRelay : function(foo, bar, hname){
14915                 if(hname == 'down' || this.scope.isExpanded()){
14916                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14917                 }
14918                 return true;
14919             },
14920
14921             forceKeyDown: true
14922         });
14923         
14924         
14925         this.queryDelay = Math.max(this.queryDelay || 10,
14926                 this.mode == 'local' ? 10 : 250);
14927         
14928         
14929         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14930         
14931         if(this.typeAhead){
14932             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14933         }
14934         if(this.editable !== false){
14935             this.inputEl().on("keyup", this.onKeyUp, this);
14936         }
14937         if(this.forceSelection){
14938             this.inputEl().on('blur', this.doForce, this);
14939         }
14940         
14941         if(this.multiple){
14942             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14943             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14944         }
14945     },
14946     
14947     initTickableEvents: function()
14948     {   
14949         this.createList();
14950         
14951         if(this.hiddenName){
14952             
14953             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14954             
14955             this.hiddenField.dom.value =
14956                 this.hiddenValue !== undefined ? this.hiddenValue :
14957                 this.value !== undefined ? this.value : '';
14958
14959             // prevent input submission
14960             this.el.dom.removeAttribute('name');
14961             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14962              
14963              
14964         }
14965         
14966 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14967         
14968         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14969         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14970         if(this.triggerList){
14971             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14972         }
14973          
14974         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14975         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14976         
14977         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14978         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14979         
14980         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14981         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14982         
14983         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14984         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14985         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14986         
14987         this.okBtn.hide();
14988         this.cancelBtn.hide();
14989         
14990         var _this = this;
14991         
14992         (function(){
14993             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14994             _this.list.setWidth(lw);
14995         }).defer(100);
14996         
14997         this.list.on('mouseover', this.onViewOver, this);
14998         this.list.on('mousemove', this.onViewMove, this);
14999         
15000         this.list.on('scroll', this.onViewScroll, this);
15001         
15002         if(!this.tpl){
15003             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15004                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15005         }
15006
15007         this.view = new Roo.View(this.list, this.tpl, {
15008             singleSelect:true,
15009             tickable:true,
15010             parent:this,
15011             store: this.store,
15012             selectedClass: this.selectedClass
15013         });
15014         
15015         //this.view.wrapEl.setDisplayed(false);
15016         this.view.on('click', this.onViewClick, this);
15017         
15018         
15019         
15020         this.store.on('beforeload', this.onBeforeLoad, this);
15021         this.store.on('load', this.onLoad, this);
15022         this.store.on('loadexception', this.onLoadException, this);
15023         
15024         if(this.editable){
15025             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15026                 "up" : function(e){
15027                     this.inKeyMode = true;
15028                     this.selectPrev();
15029                 },
15030
15031                 "down" : function(e){
15032                     this.inKeyMode = true;
15033                     this.selectNext();
15034                 },
15035
15036                 "enter" : function(e){
15037                     if(this.fireEvent("specialkey", this, e)){
15038                         this.onViewClick(false);
15039                     }
15040                     
15041                     return true;
15042                 },
15043
15044                 "esc" : function(e){
15045                     this.onTickableFooterButtonClick(e, false, false);
15046                 },
15047
15048                 "tab" : function(e){
15049                     this.fireEvent("specialkey", this, e);
15050                     
15051                     this.onTickableFooterButtonClick(e, false, false);
15052                     
15053                     return true;
15054                 },
15055
15056                 scope : this,
15057
15058                 doRelay : function(e, fn, key){
15059                     if(this.scope.isExpanded()){
15060                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15061                     }
15062                     return true;
15063                 },
15064
15065                 forceKeyDown: true
15066             });
15067         }
15068         
15069         this.queryDelay = Math.max(this.queryDelay || 10,
15070                 this.mode == 'local' ? 10 : 250);
15071         
15072         
15073         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15074         
15075         if(this.typeAhead){
15076             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15077         }
15078         
15079         if(this.editable !== false){
15080             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15081         }
15082         
15083         this.indicator = this.indicatorEl();
15084         
15085         if(this.indicator){
15086             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15087             this.indicator.hide();
15088         }
15089         
15090     },
15091
15092     onDestroy : function(){
15093         if(this.view){
15094             this.view.setStore(null);
15095             this.view.el.removeAllListeners();
15096             this.view.el.remove();
15097             this.view.purgeListeners();
15098         }
15099         if(this.list){
15100             this.list.dom.innerHTML  = '';
15101         }
15102         
15103         if(this.store){
15104             this.store.un('beforeload', this.onBeforeLoad, this);
15105             this.store.un('load', this.onLoad, this);
15106             this.store.un('loadexception', this.onLoadException, this);
15107         }
15108         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15109     },
15110
15111     // private
15112     fireKey : function(e){
15113         if(e.isNavKeyPress() && !this.list.isVisible()){
15114             this.fireEvent("specialkey", this, e);
15115         }
15116     },
15117
15118     // private
15119     onResize: function(w, h){
15120 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15121 //        
15122 //        if(typeof w != 'number'){
15123 //            // we do not handle it!?!?
15124 //            return;
15125 //        }
15126 //        var tw = this.trigger.getWidth();
15127 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15128 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15129 //        var x = w - tw;
15130 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15131 //            
15132 //        //this.trigger.setStyle('left', x+'px');
15133 //        
15134 //        if(this.list && this.listWidth === undefined){
15135 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15136 //            this.list.setWidth(lw);
15137 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15138 //        }
15139         
15140     
15141         
15142     },
15143
15144     /**
15145      * Allow or prevent the user from directly editing the field text.  If false is passed,
15146      * the user will only be able to select from the items defined in the dropdown list.  This method
15147      * is the runtime equivalent of setting the 'editable' config option at config time.
15148      * @param {Boolean} value True to allow the user to directly edit the field text
15149      */
15150     setEditable : function(value){
15151         if(value == this.editable){
15152             return;
15153         }
15154         this.editable = value;
15155         if(!value){
15156             this.inputEl().dom.setAttribute('readOnly', true);
15157             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15158             this.inputEl().addClass('x-combo-noedit');
15159         }else{
15160             this.inputEl().dom.setAttribute('readOnly', false);
15161             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15162             this.inputEl().removeClass('x-combo-noedit');
15163         }
15164     },
15165
15166     // private
15167     
15168     onBeforeLoad : function(combo,opts){
15169         if(!this.hasFocus){
15170             return;
15171         }
15172          if (!opts.add) {
15173             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15174          }
15175         this.restrictHeight();
15176         this.selectedIndex = -1;
15177     },
15178
15179     // private
15180     onLoad : function(){
15181         
15182         this.hasQuery = false;
15183         
15184         if(!this.hasFocus){
15185             return;
15186         }
15187         
15188         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15189             this.loading.hide();
15190         }
15191         
15192         if(this.store.getCount() > 0){
15193             
15194             this.expand();
15195             this.restrictHeight();
15196             if(this.lastQuery == this.allQuery){
15197                 if(this.editable && !this.tickable){
15198                     this.inputEl().dom.select();
15199                 }
15200                 
15201                 if(
15202                     !this.selectByValue(this.value, true) &&
15203                     this.autoFocus && 
15204                     (
15205                         !this.store.lastOptions ||
15206                         typeof(this.store.lastOptions.add) == 'undefined' || 
15207                         this.store.lastOptions.add != true
15208                     )
15209                 ){
15210                     this.select(0, true);
15211                 }
15212             }else{
15213                 if(this.autoFocus){
15214                     this.selectNext();
15215                 }
15216                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15217                     this.taTask.delay(this.typeAheadDelay);
15218                 }
15219             }
15220         }else{
15221             this.onEmptyResults();
15222         }
15223         
15224         //this.el.focus();
15225     },
15226     // private
15227     onLoadException : function()
15228     {
15229         this.hasQuery = false;
15230         
15231         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15232             this.loading.hide();
15233         }
15234         
15235         if(this.tickable && this.editable){
15236             return;
15237         }
15238         
15239         this.collapse();
15240         // only causes errors at present
15241         //Roo.log(this.store.reader.jsonData);
15242         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15243             // fixme
15244             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15245         //}
15246         
15247         
15248     },
15249     // private
15250     onTypeAhead : function(){
15251         if(this.store.getCount() > 0){
15252             var r = this.store.getAt(0);
15253             var newValue = r.data[this.displayField];
15254             var len = newValue.length;
15255             var selStart = this.getRawValue().length;
15256             
15257             if(selStart != len){
15258                 this.setRawValue(newValue);
15259                 this.selectText(selStart, newValue.length);
15260             }
15261         }
15262     },
15263
15264     // private
15265     onSelect : function(record, index){
15266         
15267         if(this.fireEvent('beforeselect', this, record, index) !== false){
15268         
15269             this.setFromData(index > -1 ? record.data : false);
15270             
15271             this.collapse();
15272             this.fireEvent('select', this, record, index);
15273         }
15274     },
15275
15276     /**
15277      * Returns the currently selected field value or empty string if no value is set.
15278      * @return {String} value The selected value
15279      */
15280     getValue : function()
15281     {
15282         if(Roo.isIOS && this.useNativeIOS){
15283             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15284         }
15285         
15286         if(this.multiple){
15287             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15288         }
15289         
15290         if(this.valueField){
15291             return typeof this.value != 'undefined' ? this.value : '';
15292         }else{
15293             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15294         }
15295     },
15296     
15297     getRawValue : function()
15298     {
15299         if(Roo.isIOS && this.useNativeIOS){
15300             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15301         }
15302         
15303         var v = this.inputEl().getValue();
15304         
15305         return v;
15306     },
15307
15308     /**
15309      * Clears any text/value currently set in the field
15310      */
15311     clearValue : function(){
15312         
15313         if(this.hiddenField){
15314             this.hiddenField.dom.value = '';
15315         }
15316         this.value = '';
15317         this.setRawValue('');
15318         this.lastSelectionText = '';
15319         this.lastData = false;
15320         
15321         var close = this.closeTriggerEl();
15322         
15323         if(close){
15324             close.hide();
15325         }
15326         
15327         this.validate();
15328         
15329     },
15330
15331     /**
15332      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15333      * will be displayed in the field.  If the value does not match the data value of an existing item,
15334      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15335      * Otherwise the field will be blank (although the value will still be set).
15336      * @param {String} value The value to match
15337      */
15338     setValue : function(v)
15339     {
15340         if(Roo.isIOS && this.useNativeIOS){
15341             this.setIOSValue(v);
15342             return;
15343         }
15344         
15345         if(this.multiple){
15346             this.syncValue();
15347             return;
15348         }
15349         
15350         var text = v;
15351         if(this.valueField){
15352             var r = this.findRecord(this.valueField, v);
15353             if(r){
15354                 text = r.data[this.displayField];
15355             }else if(this.valueNotFoundText !== undefined){
15356                 text = this.valueNotFoundText;
15357             }
15358         }
15359         this.lastSelectionText = text;
15360         if(this.hiddenField){
15361             this.hiddenField.dom.value = v;
15362         }
15363         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15364         this.value = v;
15365         
15366         var close = this.closeTriggerEl();
15367         
15368         if(close){
15369             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15370         }
15371         
15372         this.validate();
15373     },
15374     /**
15375      * @property {Object} the last set data for the element
15376      */
15377     
15378     lastData : false,
15379     /**
15380      * Sets the value of the field based on a object which is related to the record format for the store.
15381      * @param {Object} value the value to set as. or false on reset?
15382      */
15383     setFromData : function(o){
15384         
15385         if(this.multiple){
15386             this.addItem(o);
15387             return;
15388         }
15389             
15390         var dv = ''; // display value
15391         var vv = ''; // value value..
15392         this.lastData = o;
15393         if (this.displayField) {
15394             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15395         } else {
15396             // this is an error condition!!!
15397             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15398         }
15399         
15400         if(this.valueField){
15401             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15402         }
15403         
15404         var close = this.closeTriggerEl();
15405         
15406         if(close){
15407             if(dv.length || vv * 1 > 0){
15408                 close.show() ;
15409                 this.blockFocus=true;
15410             } else {
15411                 close.hide();
15412             }             
15413         }
15414         
15415         if(this.hiddenField){
15416             this.hiddenField.dom.value = vv;
15417             
15418             this.lastSelectionText = dv;
15419             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15420             this.value = vv;
15421             return;
15422         }
15423         // no hidden field.. - we store the value in 'value', but still display
15424         // display field!!!!
15425         this.lastSelectionText = dv;
15426         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15427         this.value = vv;
15428         
15429         
15430         
15431     },
15432     // private
15433     reset : function(){
15434         // overridden so that last data is reset..
15435         
15436         if(this.multiple){
15437             this.clearItem();
15438             return;
15439         }
15440         
15441         this.setValue(this.originalValue);
15442         //this.clearInvalid();
15443         this.lastData = false;
15444         if (this.view) {
15445             this.view.clearSelections();
15446         }
15447         
15448         this.validate();
15449     },
15450     // private
15451     findRecord : function(prop, value){
15452         var record;
15453         if(this.store.getCount() > 0){
15454             this.store.each(function(r){
15455                 if(r.data[prop] == value){
15456                     record = r;
15457                     return false;
15458                 }
15459                 return true;
15460             });
15461         }
15462         return record;
15463     },
15464     
15465     getName: function()
15466     {
15467         // returns hidden if it's set..
15468         if (!this.rendered) {return ''};
15469         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15470         
15471     },
15472     // private
15473     onViewMove : function(e, t){
15474         this.inKeyMode = false;
15475     },
15476
15477     // private
15478     onViewOver : function(e, t){
15479         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15480             return;
15481         }
15482         var item = this.view.findItemFromChild(t);
15483         
15484         if(item){
15485             var index = this.view.indexOf(item);
15486             this.select(index, false);
15487         }
15488     },
15489
15490     // private
15491     onViewClick : function(view, doFocus, el, e)
15492     {
15493         var index = this.view.getSelectedIndexes()[0];
15494         
15495         var r = this.store.getAt(index);
15496         
15497         if(this.tickable){
15498             
15499             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15500                 return;
15501             }
15502             
15503             var rm = false;
15504             var _this = this;
15505             
15506             Roo.each(this.tickItems, function(v,k){
15507                 
15508                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15509                     Roo.log(v);
15510                     _this.tickItems.splice(k, 1);
15511                     
15512                     if(typeof(e) == 'undefined' && view == false){
15513                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15514                     }
15515                     
15516                     rm = true;
15517                     return;
15518                 }
15519             });
15520             
15521             if(rm){
15522                 return;
15523             }
15524             
15525             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15526                 this.tickItems.push(r.data);
15527             }
15528             
15529             if(typeof(e) == 'undefined' && view == false){
15530                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15531             }
15532                     
15533             return;
15534         }
15535         
15536         if(r){
15537             this.onSelect(r, index);
15538         }
15539         if(doFocus !== false && !this.blockFocus){
15540             this.inputEl().focus();
15541         }
15542     },
15543
15544     // private
15545     restrictHeight : function(){
15546         //this.innerList.dom.style.height = '';
15547         //var inner = this.innerList.dom;
15548         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15549         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15550         //this.list.beginUpdate();
15551         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15552         this.list.alignTo(this.inputEl(), this.listAlign);
15553         this.list.alignTo(this.inputEl(), this.listAlign);
15554         //this.list.endUpdate();
15555     },
15556
15557     // private
15558     onEmptyResults : function(){
15559         
15560         if(this.tickable && this.editable){
15561             this.hasFocus = false;
15562             this.restrictHeight();
15563             return;
15564         }
15565         
15566         this.collapse();
15567     },
15568
15569     /**
15570      * Returns true if the dropdown list is expanded, else false.
15571      */
15572     isExpanded : function(){
15573         return this.list.isVisible();
15574     },
15575
15576     /**
15577      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15578      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15579      * @param {String} value The data value of the item to select
15580      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15581      * selected item if it is not currently in view (defaults to true)
15582      * @return {Boolean} True if the value matched an item in the list, else false
15583      */
15584     selectByValue : function(v, scrollIntoView){
15585         if(v !== undefined && v !== null){
15586             var r = this.findRecord(this.valueField || this.displayField, v);
15587             if(r){
15588                 this.select(this.store.indexOf(r), scrollIntoView);
15589                 return true;
15590             }
15591         }
15592         return false;
15593     },
15594
15595     /**
15596      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15597      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15598      * @param {Number} index The zero-based index of the list item to select
15599      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15600      * selected item if it is not currently in view (defaults to true)
15601      */
15602     select : function(index, scrollIntoView){
15603         this.selectedIndex = index;
15604         this.view.select(index);
15605         if(scrollIntoView !== false){
15606             var el = this.view.getNode(index);
15607             /*
15608              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15609              */
15610             if(el){
15611                 this.list.scrollChildIntoView(el, false);
15612             }
15613         }
15614     },
15615
15616     // private
15617     selectNext : function(){
15618         var ct = this.store.getCount();
15619         if(ct > 0){
15620             if(this.selectedIndex == -1){
15621                 this.select(0);
15622             }else if(this.selectedIndex < ct-1){
15623                 this.select(this.selectedIndex+1);
15624             }
15625         }
15626     },
15627
15628     // private
15629     selectPrev : function(){
15630         var ct = this.store.getCount();
15631         if(ct > 0){
15632             if(this.selectedIndex == -1){
15633                 this.select(0);
15634             }else if(this.selectedIndex != 0){
15635                 this.select(this.selectedIndex-1);
15636             }
15637         }
15638     },
15639
15640     // private
15641     onKeyUp : function(e){
15642         if(this.editable !== false && !e.isSpecialKey()){
15643             this.lastKey = e.getKey();
15644             this.dqTask.delay(this.queryDelay);
15645         }
15646     },
15647
15648     // private
15649     validateBlur : function(){
15650         return !this.list || !this.list.isVisible();   
15651     },
15652
15653     // private
15654     initQuery : function(){
15655         
15656         var v = this.getRawValue();
15657         
15658         if(this.tickable && this.editable){
15659             v = this.tickableInputEl().getValue();
15660         }
15661         
15662         this.doQuery(v);
15663     },
15664
15665     // private
15666     doForce : function(){
15667         if(this.inputEl().dom.value.length > 0){
15668             this.inputEl().dom.value =
15669                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15670              
15671         }
15672     },
15673
15674     /**
15675      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15676      * query allowing the query action to be canceled if needed.
15677      * @param {String} query The SQL query to execute
15678      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15679      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15680      * saved in the current store (defaults to false)
15681      */
15682     doQuery : function(q, forceAll){
15683         
15684         if(q === undefined || q === null){
15685             q = '';
15686         }
15687         var qe = {
15688             query: q,
15689             forceAll: forceAll,
15690             combo: this,
15691             cancel:false
15692         };
15693         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15694             return false;
15695         }
15696         q = qe.query;
15697         
15698         forceAll = qe.forceAll;
15699         if(forceAll === true || (q.length >= this.minChars)){
15700             
15701             this.hasQuery = true;
15702             
15703             if(this.lastQuery != q || this.alwaysQuery){
15704                 this.lastQuery = q;
15705                 if(this.mode == 'local'){
15706                     this.selectedIndex = -1;
15707                     if(forceAll){
15708                         this.store.clearFilter();
15709                     }else{
15710                         
15711                         if(this.specialFilter){
15712                             this.fireEvent('specialfilter', this);
15713                             this.onLoad();
15714                             return;
15715                         }
15716                         
15717                         this.store.filter(this.displayField, q);
15718                     }
15719                     
15720                     this.store.fireEvent("datachanged", this.store);
15721                     
15722                     this.onLoad();
15723                     
15724                     
15725                 }else{
15726                     
15727                     this.store.baseParams[this.queryParam] = q;
15728                     
15729                     var options = {params : this.getParams(q)};
15730                     
15731                     if(this.loadNext){
15732                         options.add = true;
15733                         options.params.start = this.page * this.pageSize;
15734                     }
15735                     
15736                     this.store.load(options);
15737                     
15738                     /*
15739                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15740                      *  we should expand the list on onLoad
15741                      *  so command out it
15742                      */
15743 //                    this.expand();
15744                 }
15745             }else{
15746                 this.selectedIndex = -1;
15747                 this.onLoad();   
15748             }
15749         }
15750         
15751         this.loadNext = false;
15752     },
15753     
15754     // private
15755     getParams : function(q){
15756         var p = {};
15757         //p[this.queryParam] = q;
15758         
15759         if(this.pageSize){
15760             p.start = 0;
15761             p.limit = this.pageSize;
15762         }
15763         return p;
15764     },
15765
15766     /**
15767      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15768      */
15769     collapse : function(){
15770         if(!this.isExpanded()){
15771             return;
15772         }
15773         
15774         this.list.hide();
15775         
15776         this.hasFocus = false;
15777         
15778         if(this.tickable){
15779             this.okBtn.hide();
15780             this.cancelBtn.hide();
15781             this.trigger.show();
15782             
15783             if(this.editable){
15784                 this.tickableInputEl().dom.value = '';
15785                 this.tickableInputEl().blur();
15786             }
15787             
15788         }
15789         
15790         Roo.get(document).un('mousedown', this.collapseIf, this);
15791         Roo.get(document).un('mousewheel', this.collapseIf, this);
15792         if (!this.editable) {
15793             Roo.get(document).un('keydown', this.listKeyPress, this);
15794         }
15795         this.fireEvent('collapse', this);
15796         
15797         this.validate();
15798     },
15799
15800     // private
15801     collapseIf : function(e){
15802         var in_combo  = e.within(this.el);
15803         var in_list =  e.within(this.list);
15804         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15805         
15806         if (in_combo || in_list || is_list) {
15807             //e.stopPropagation();
15808             return;
15809         }
15810         
15811         if(this.tickable){
15812             this.onTickableFooterButtonClick(e, false, false);
15813         }
15814
15815         this.collapse();
15816         
15817     },
15818
15819     /**
15820      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15821      */
15822     expand : function(){
15823        
15824         if(this.isExpanded() || !this.hasFocus){
15825             return;
15826         }
15827         
15828         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15829         this.list.setWidth(lw);
15830         
15831         Roo.log('expand');
15832         
15833         this.list.show();
15834         
15835         this.restrictHeight();
15836         
15837         if(this.tickable){
15838             
15839             this.tickItems = Roo.apply([], this.item);
15840             
15841             this.okBtn.show();
15842             this.cancelBtn.show();
15843             this.trigger.hide();
15844             
15845             if(this.editable){
15846                 this.tickableInputEl().focus();
15847             }
15848             
15849         }
15850         
15851         Roo.get(document).on('mousedown', this.collapseIf, this);
15852         Roo.get(document).on('mousewheel', this.collapseIf, this);
15853         if (!this.editable) {
15854             Roo.get(document).on('keydown', this.listKeyPress, this);
15855         }
15856         
15857         this.fireEvent('expand', this);
15858     },
15859
15860     // private
15861     // Implements the default empty TriggerField.onTriggerClick function
15862     onTriggerClick : function(e)
15863     {
15864         Roo.log('trigger click');
15865         
15866         if(this.disabled || !this.triggerList){
15867             return;
15868         }
15869         
15870         this.page = 0;
15871         this.loadNext = false;
15872         
15873         if(this.isExpanded()){
15874             this.collapse();
15875             if (!this.blockFocus) {
15876                 this.inputEl().focus();
15877             }
15878             
15879         }else {
15880             this.hasFocus = true;
15881             if(this.triggerAction == 'all') {
15882                 this.doQuery(this.allQuery, true);
15883             } else {
15884                 this.doQuery(this.getRawValue());
15885             }
15886             if (!this.blockFocus) {
15887                 this.inputEl().focus();
15888             }
15889         }
15890     },
15891     
15892     onTickableTriggerClick : function(e)
15893     {
15894         if(this.disabled){
15895             return;
15896         }
15897         
15898         this.page = 0;
15899         this.loadNext = false;
15900         this.hasFocus = true;
15901         
15902         if(this.triggerAction == 'all') {
15903             this.doQuery(this.allQuery, true);
15904         } else {
15905             this.doQuery(this.getRawValue());
15906         }
15907     },
15908     
15909     onSearchFieldClick : function(e)
15910     {
15911         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15912             this.onTickableFooterButtonClick(e, false, false);
15913             return;
15914         }
15915         
15916         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15917             return;
15918         }
15919         
15920         this.page = 0;
15921         this.loadNext = false;
15922         this.hasFocus = true;
15923         
15924         if(this.triggerAction == 'all') {
15925             this.doQuery(this.allQuery, true);
15926         } else {
15927             this.doQuery(this.getRawValue());
15928         }
15929     },
15930     
15931     listKeyPress : function(e)
15932     {
15933         //Roo.log('listkeypress');
15934         // scroll to first matching element based on key pres..
15935         if (e.isSpecialKey()) {
15936             return false;
15937         }
15938         var k = String.fromCharCode(e.getKey()).toUpperCase();
15939         //Roo.log(k);
15940         var match  = false;
15941         var csel = this.view.getSelectedNodes();
15942         var cselitem = false;
15943         if (csel.length) {
15944             var ix = this.view.indexOf(csel[0]);
15945             cselitem  = this.store.getAt(ix);
15946             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15947                 cselitem = false;
15948             }
15949             
15950         }
15951         
15952         this.store.each(function(v) { 
15953             if (cselitem) {
15954                 // start at existing selection.
15955                 if (cselitem.id == v.id) {
15956                     cselitem = false;
15957                 }
15958                 return true;
15959             }
15960                 
15961             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15962                 match = this.store.indexOf(v);
15963                 return false;
15964             }
15965             return true;
15966         }, this);
15967         
15968         if (match === false) {
15969             return true; // no more action?
15970         }
15971         // scroll to?
15972         this.view.select(match);
15973         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15974         sn.scrollIntoView(sn.dom.parentNode, false);
15975     },
15976     
15977     onViewScroll : function(e, t){
15978         
15979         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){
15980             return;
15981         }
15982         
15983         this.hasQuery = true;
15984         
15985         this.loading = this.list.select('.loading', true).first();
15986         
15987         if(this.loading === null){
15988             this.list.createChild({
15989                 tag: 'div',
15990                 cls: 'loading roo-select2-more-results roo-select2-active',
15991                 html: 'Loading more results...'
15992             });
15993             
15994             this.loading = this.list.select('.loading', true).first();
15995             
15996             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15997             
15998             this.loading.hide();
15999         }
16000         
16001         this.loading.show();
16002         
16003         var _combo = this;
16004         
16005         this.page++;
16006         this.loadNext = true;
16007         
16008         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16009         
16010         return;
16011     },
16012     
16013     addItem : function(o)
16014     {   
16015         var dv = ''; // display value
16016         
16017         if (this.displayField) {
16018             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16019         } else {
16020             // this is an error condition!!!
16021             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16022         }
16023         
16024         if(!dv.length){
16025             return;
16026         }
16027         
16028         var choice = this.choices.createChild({
16029             tag: 'li',
16030             cls: 'roo-select2-search-choice',
16031             cn: [
16032                 {
16033                     tag: 'div',
16034                     html: dv
16035                 },
16036                 {
16037                     tag: 'a',
16038                     href: '#',
16039                     cls: 'roo-select2-search-choice-close fa fa-times',
16040                     tabindex: '-1'
16041                 }
16042             ]
16043             
16044         }, this.searchField);
16045         
16046         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16047         
16048         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16049         
16050         this.item.push(o);
16051         
16052         this.lastData = o;
16053         
16054         this.syncValue();
16055         
16056         this.inputEl().dom.value = '';
16057         
16058         this.validate();
16059     },
16060     
16061     onRemoveItem : function(e, _self, o)
16062     {
16063         e.preventDefault();
16064         
16065         this.lastItem = Roo.apply([], this.item);
16066         
16067         var index = this.item.indexOf(o.data) * 1;
16068         
16069         if( index < 0){
16070             Roo.log('not this item?!');
16071             return;
16072         }
16073         
16074         this.item.splice(index, 1);
16075         o.item.remove();
16076         
16077         this.syncValue();
16078         
16079         this.fireEvent('remove', this, e);
16080         
16081         this.validate();
16082         
16083     },
16084     
16085     syncValue : function()
16086     {
16087         if(!this.item.length){
16088             this.clearValue();
16089             return;
16090         }
16091             
16092         var value = [];
16093         var _this = this;
16094         Roo.each(this.item, function(i){
16095             if(_this.valueField){
16096                 value.push(i[_this.valueField]);
16097                 return;
16098             }
16099
16100             value.push(i);
16101         });
16102
16103         this.value = value.join(',');
16104
16105         if(this.hiddenField){
16106             this.hiddenField.dom.value = this.value;
16107         }
16108         
16109         this.store.fireEvent("datachanged", this.store);
16110         
16111         this.validate();
16112     },
16113     
16114     clearItem : function()
16115     {
16116         if(!this.multiple){
16117             return;
16118         }
16119         
16120         this.item = [];
16121         
16122         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16123            c.remove();
16124         });
16125         
16126         this.syncValue();
16127         
16128         this.validate();
16129         
16130         if(this.tickable && !Roo.isTouch){
16131             this.view.refresh();
16132         }
16133     },
16134     
16135     inputEl: function ()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.el.select('select.roo-ios-select', true).first();
16139         }
16140         
16141         if(Roo.isTouch && this.mobileTouchView){
16142             return this.el.select('input.form-control',true).first();
16143         }
16144         
16145         if(this.tickable){
16146             return this.searchField;
16147         }
16148         
16149         return this.el.select('input.form-control',true).first();
16150     },
16151     
16152     onTickableFooterButtonClick : function(e, btn, el)
16153     {
16154         e.preventDefault();
16155         
16156         this.lastItem = Roo.apply([], this.item);
16157         
16158         if(btn && btn.name == 'cancel'){
16159             this.tickItems = Roo.apply([], this.item);
16160             this.collapse();
16161             return;
16162         }
16163         
16164         this.clearItem();
16165         
16166         var _this = this;
16167         
16168         Roo.each(this.tickItems, function(o){
16169             _this.addItem(o);
16170         });
16171         
16172         this.collapse();
16173         
16174     },
16175     
16176     validate : function()
16177     {
16178         if(this.getVisibilityEl().hasClass('hidden')){
16179             return true;
16180         }
16181         
16182         var v = this.getRawValue();
16183         
16184         if(this.multiple){
16185             v = this.getValue();
16186         }
16187         
16188         if(this.disabled || this.allowBlank || v.length){
16189             this.markValid();
16190             return true;
16191         }
16192         
16193         this.markInvalid();
16194         return false;
16195     },
16196     
16197     tickableInputEl : function()
16198     {
16199         if(!this.tickable || !this.editable){
16200             return this.inputEl();
16201         }
16202         
16203         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16204     },
16205     
16206     
16207     getAutoCreateTouchView : function()
16208     {
16209         var id = Roo.id();
16210         
16211         var cfg = {
16212             cls: 'form-group' //input-group
16213         };
16214         
16215         var input =  {
16216             tag: 'input',
16217             id : id,
16218             type : this.inputType,
16219             cls : 'form-control x-combo-noedit',
16220             autocomplete: 'new-password',
16221             placeholder : this.placeholder || '',
16222             readonly : true
16223         };
16224         
16225         if (this.name) {
16226             input.name = this.name;
16227         }
16228         
16229         if (this.size) {
16230             input.cls += ' input-' + this.size;
16231         }
16232         
16233         if (this.disabled) {
16234             input.disabled = true;
16235         }
16236         
16237         var inputblock = {
16238             cls : '',
16239             cn : [
16240                 input
16241             ]
16242         };
16243         
16244         if(this.before){
16245             inputblock.cls += ' input-group';
16246             
16247             inputblock.cn.unshift({
16248                 tag :'span',
16249                 cls : 'input-group-addon input-group-prepend input-group-text',
16250                 html : this.before
16251             });
16252         }
16253         
16254         if(this.removable && !this.multiple){
16255             inputblock.cls += ' roo-removable';
16256             
16257             inputblock.cn.push({
16258                 tag: 'button',
16259                 html : 'x',
16260                 cls : 'roo-combo-removable-btn close'
16261             });
16262         }
16263
16264         if(this.hasFeedback && !this.allowBlank){
16265             
16266             inputblock.cls += ' has-feedback';
16267             
16268             inputblock.cn.push({
16269                 tag: 'span',
16270                 cls: 'glyphicon form-control-feedback'
16271             });
16272             
16273         }
16274         
16275         if (this.after) {
16276             
16277             inputblock.cls += (this.before) ? '' : ' input-group';
16278             
16279             inputblock.cn.push({
16280                 tag :'span',
16281                 cls : 'input-group-addon input-group-append input-group-text',
16282                 html : this.after
16283             });
16284         }
16285
16286         
16287         var ibwrap = inputblock;
16288         
16289         if(this.multiple){
16290             ibwrap = {
16291                 tag: 'ul',
16292                 cls: 'roo-select2-choices',
16293                 cn:[
16294                     {
16295                         tag: 'li',
16296                         cls: 'roo-select2-search-field',
16297                         cn: [
16298
16299                             inputblock
16300                         ]
16301                     }
16302                 ]
16303             };
16304         
16305             
16306         }
16307         
16308         var combobox = {
16309             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16310             cn: [
16311                 {
16312                     tag: 'input',
16313                     type : 'hidden',
16314                     cls: 'form-hidden-field'
16315                 },
16316                 ibwrap
16317             ]
16318         };
16319         
16320         if(!this.multiple && this.showToggleBtn){
16321             
16322             var caret = {
16323                 cls: 'caret'
16324             };
16325             
16326             if (this.caret != false) {
16327                 caret = {
16328                      tag: 'i',
16329                      cls: 'fa fa-' + this.caret
16330                 };
16331                 
16332             }
16333             
16334             combobox.cn.push({
16335                 tag :'span',
16336                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16337                 cn : [
16338                     Roo.bootstrap.version == 3 ? caret : '',
16339                     {
16340                         tag: 'span',
16341                         cls: 'combobox-clear',
16342                         cn  : [
16343                             {
16344                                 tag : 'i',
16345                                 cls: 'icon-remove'
16346                             }
16347                         ]
16348                     }
16349                 ]
16350
16351             })
16352         }
16353         
16354         if(this.multiple){
16355             combobox.cls += ' roo-select2-container-multi';
16356         }
16357         
16358         var align = this.labelAlign || this.parentLabelAlign();
16359         
16360         if (align ==='left' && this.fieldLabel.length) {
16361
16362             cfg.cn = [
16363                 {
16364                    tag : 'i',
16365                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16366                    tooltip : 'This field is required'
16367                 },
16368                 {
16369                     tag: 'label',
16370                     cls : 'control-label col-form-label',
16371                     html : this.fieldLabel
16372
16373                 },
16374                 {
16375                     cls : '', 
16376                     cn: [
16377                         combobox
16378                     ]
16379                 }
16380             ];
16381             
16382             var labelCfg = cfg.cn[1];
16383             var contentCfg = cfg.cn[2];
16384             
16385
16386             if(this.indicatorpos == 'right'){
16387                 cfg.cn = [
16388                     {
16389                         tag: 'label',
16390                         'for' :  id,
16391                         cls : 'control-label col-form-label',
16392                         cn : [
16393                             {
16394                                 tag : 'span',
16395                                 html : this.fieldLabel
16396                             },
16397                             {
16398                                 tag : 'i',
16399                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16400                                 tooltip : 'This field is required'
16401                             }
16402                         ]
16403                     },
16404                     {
16405                         cls : "",
16406                         cn: [
16407                             combobox
16408                         ]
16409                     }
16410
16411                 ];
16412                 
16413                 labelCfg = cfg.cn[0];
16414                 contentCfg = cfg.cn[1];
16415             }
16416             
16417            
16418             
16419             if(this.labelWidth > 12){
16420                 labelCfg.style = "width: " + this.labelWidth + 'px';
16421             }
16422             
16423             if(this.labelWidth < 13 && this.labelmd == 0){
16424                 this.labelmd = this.labelWidth;
16425             }
16426             
16427             if(this.labellg > 0){
16428                 labelCfg.cls += ' col-lg-' + this.labellg;
16429                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16430             }
16431             
16432             if(this.labelmd > 0){
16433                 labelCfg.cls += ' col-md-' + this.labelmd;
16434                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16435             }
16436             
16437             if(this.labelsm > 0){
16438                 labelCfg.cls += ' col-sm-' + this.labelsm;
16439                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16440             }
16441             
16442             if(this.labelxs > 0){
16443                 labelCfg.cls += ' col-xs-' + this.labelxs;
16444                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16445             }
16446                 
16447                 
16448         } else if ( this.fieldLabel.length) {
16449             cfg.cn = [
16450                 {
16451                    tag : 'i',
16452                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16453                    tooltip : 'This field is required'
16454                 },
16455                 {
16456                     tag: 'label',
16457                     cls : 'control-label',
16458                     html : this.fieldLabel
16459
16460                 },
16461                 {
16462                     cls : '', 
16463                     cn: [
16464                         combobox
16465                     ]
16466                 }
16467             ];
16468             
16469             if(this.indicatorpos == 'right'){
16470                 cfg.cn = [
16471                     {
16472                         tag: 'label',
16473                         cls : 'control-label',
16474                         html : this.fieldLabel,
16475                         cn : [
16476                             {
16477                                tag : 'i',
16478                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16479                                tooltip : 'This field is required'
16480                             }
16481                         ]
16482                     },
16483                     {
16484                         cls : '', 
16485                         cn: [
16486                             combobox
16487                         ]
16488                     }
16489                 ];
16490             }
16491         } else {
16492             cfg.cn = combobox;    
16493         }
16494         
16495         
16496         var settings = this;
16497         
16498         ['xs','sm','md','lg'].map(function(size){
16499             if (settings[size]) {
16500                 cfg.cls += ' col-' + size + '-' + settings[size];
16501             }
16502         });
16503         
16504         return cfg;
16505     },
16506     
16507     initTouchView : function()
16508     {
16509         this.renderTouchView();
16510         
16511         this.touchViewEl.on('scroll', function(){
16512             this.el.dom.scrollTop = 0;
16513         }, this);
16514         
16515         this.originalValue = this.getValue();
16516         
16517         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16518         
16519         this.inputEl().on("click", this.showTouchView, this);
16520         if (this.triggerEl) {
16521             this.triggerEl.on("click", this.showTouchView, this);
16522         }
16523         
16524         
16525         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16526         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16527         
16528         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16529         
16530         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16531         this.store.on('load', this.onTouchViewLoad, this);
16532         this.store.on('loadexception', this.onTouchViewLoadException, this);
16533         
16534         if(this.hiddenName){
16535             
16536             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16537             
16538             this.hiddenField.dom.value =
16539                 this.hiddenValue !== undefined ? this.hiddenValue :
16540                 this.value !== undefined ? this.value : '';
16541         
16542             this.el.dom.removeAttribute('name');
16543             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16544         }
16545         
16546         if(this.multiple){
16547             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16548             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16549         }
16550         
16551         if(this.removable && !this.multiple){
16552             var close = this.closeTriggerEl();
16553             if(close){
16554                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16555                 close.on('click', this.removeBtnClick, this, close);
16556             }
16557         }
16558         /*
16559          * fix the bug in Safari iOS8
16560          */
16561         this.inputEl().on("focus", function(e){
16562             document.activeElement.blur();
16563         }, this);
16564         
16565         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16566         
16567         return;
16568         
16569         
16570     },
16571     
16572     renderTouchView : function()
16573     {
16574         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16575         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16576         
16577         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16578         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16579         
16580         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16581         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16582         this.touchViewBodyEl.setStyle('overflow', 'auto');
16583         
16584         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16585         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16586         
16587         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16588         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16589         
16590     },
16591     
16592     showTouchView : function()
16593     {
16594         if(this.disabled){
16595             return;
16596         }
16597         
16598         this.touchViewHeaderEl.hide();
16599
16600         if(this.modalTitle.length){
16601             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16602             this.touchViewHeaderEl.show();
16603         }
16604
16605         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16606         this.touchViewEl.show();
16607
16608         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16609         
16610         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16611         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16612
16613         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16614
16615         if(this.modalTitle.length){
16616             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16617         }
16618         
16619         this.touchViewBodyEl.setHeight(bodyHeight);
16620
16621         if(this.animate){
16622             var _this = this;
16623             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16624         }else{
16625             this.touchViewEl.addClass('in');
16626         }
16627         
16628         if(this._touchViewMask){
16629             Roo.get(document.body).addClass("x-body-masked");
16630             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16631             this._touchViewMask.setStyle('z-index', 10000);
16632             this._touchViewMask.addClass('show');
16633         }
16634         
16635         this.doTouchViewQuery();
16636         
16637     },
16638     
16639     hideTouchView : function()
16640     {
16641         this.touchViewEl.removeClass('in');
16642
16643         if(this.animate){
16644             var _this = this;
16645             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16646         }else{
16647             this.touchViewEl.setStyle('display', 'none');
16648         }
16649         
16650         if(this._touchViewMask){
16651             this._touchViewMask.removeClass('show');
16652             Roo.get(document.body).removeClass("x-body-masked");
16653         }
16654     },
16655     
16656     setTouchViewValue : function()
16657     {
16658         if(this.multiple){
16659             this.clearItem();
16660         
16661             var _this = this;
16662
16663             Roo.each(this.tickItems, function(o){
16664                 this.addItem(o);
16665             }, this);
16666         }
16667         
16668         this.hideTouchView();
16669     },
16670     
16671     doTouchViewQuery : function()
16672     {
16673         var qe = {
16674             query: '',
16675             forceAll: true,
16676             combo: this,
16677             cancel:false
16678         };
16679         
16680         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16681             return false;
16682         }
16683         
16684         if(!this.alwaysQuery || this.mode == 'local'){
16685             this.onTouchViewLoad();
16686             return;
16687         }
16688         
16689         this.store.load();
16690     },
16691     
16692     onTouchViewBeforeLoad : function(combo,opts)
16693     {
16694         return;
16695     },
16696
16697     // private
16698     onTouchViewLoad : function()
16699     {
16700         if(this.store.getCount() < 1){
16701             this.onTouchViewEmptyResults();
16702             return;
16703         }
16704         
16705         this.clearTouchView();
16706         
16707         var rawValue = this.getRawValue();
16708         
16709         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16710         
16711         this.tickItems = [];
16712         
16713         this.store.data.each(function(d, rowIndex){
16714             var row = this.touchViewListGroup.createChild(template);
16715             
16716             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16717                 row.addClass(d.data.cls);
16718             }
16719             
16720             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16721                 var cfg = {
16722                     data : d.data,
16723                     html : d.data[this.displayField]
16724                 };
16725                 
16726                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16727                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16728                 }
16729             }
16730             row.removeClass('selected');
16731             if(!this.multiple && this.valueField &&
16732                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16733             {
16734                 // radio buttons..
16735                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16736                 row.addClass('selected');
16737             }
16738             
16739             if(this.multiple && this.valueField &&
16740                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16741             {
16742                 
16743                 // checkboxes...
16744                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16745                 this.tickItems.push(d.data);
16746             }
16747             
16748             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16749             
16750         }, this);
16751         
16752         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16753         
16754         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16755
16756         if(this.modalTitle.length){
16757             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16758         }
16759
16760         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16761         
16762         if(this.mobile_restrict_height && listHeight < bodyHeight){
16763             this.touchViewBodyEl.setHeight(listHeight);
16764         }
16765         
16766         var _this = this;
16767         
16768         if(firstChecked && listHeight > bodyHeight){
16769             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16770         }
16771         
16772     },
16773     
16774     onTouchViewLoadException : function()
16775     {
16776         this.hideTouchView();
16777     },
16778     
16779     onTouchViewEmptyResults : function()
16780     {
16781         this.clearTouchView();
16782         
16783         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16784         
16785         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16786         
16787     },
16788     
16789     clearTouchView : function()
16790     {
16791         this.touchViewListGroup.dom.innerHTML = '';
16792     },
16793     
16794     onTouchViewClick : function(e, el, o)
16795     {
16796         e.preventDefault();
16797         
16798         var row = o.row;
16799         var rowIndex = o.rowIndex;
16800         
16801         var r = this.store.getAt(rowIndex);
16802         
16803         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16804             
16805             if(!this.multiple){
16806                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16807                     c.dom.removeAttribute('checked');
16808                 }, this);
16809
16810                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16811
16812                 this.setFromData(r.data);
16813
16814                 var close = this.closeTriggerEl();
16815
16816                 if(close){
16817                     close.show();
16818                 }
16819
16820                 this.hideTouchView();
16821
16822                 this.fireEvent('select', this, r, rowIndex);
16823
16824                 return;
16825             }
16826
16827             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16828                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16829                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16830                 return;
16831             }
16832
16833             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16834             this.addItem(r.data);
16835             this.tickItems.push(r.data);
16836         }
16837     },
16838     
16839     getAutoCreateNativeIOS : function()
16840     {
16841         var cfg = {
16842             cls: 'form-group' //input-group,
16843         };
16844         
16845         var combobox =  {
16846             tag: 'select',
16847             cls : 'roo-ios-select'
16848         };
16849         
16850         if (this.name) {
16851             combobox.name = this.name;
16852         }
16853         
16854         if (this.disabled) {
16855             combobox.disabled = true;
16856         }
16857         
16858         var settings = this;
16859         
16860         ['xs','sm','md','lg'].map(function(size){
16861             if (settings[size]) {
16862                 cfg.cls += ' col-' + size + '-' + settings[size];
16863             }
16864         });
16865         
16866         cfg.cn = combobox;
16867         
16868         return cfg;
16869         
16870     },
16871     
16872     initIOSView : function()
16873     {
16874         this.store.on('load', this.onIOSViewLoad, this);
16875         
16876         return;
16877     },
16878     
16879     onIOSViewLoad : function()
16880     {
16881         if(this.store.getCount() < 1){
16882             return;
16883         }
16884         
16885         this.clearIOSView();
16886         
16887         if(this.allowBlank) {
16888             
16889             var default_text = '-- SELECT --';
16890             
16891             if(this.placeholder.length){
16892                 default_text = this.placeholder;
16893             }
16894             
16895             if(this.emptyTitle.length){
16896                 default_text += ' - ' + this.emptyTitle + ' -';
16897             }
16898             
16899             var opt = this.inputEl().createChild({
16900                 tag: 'option',
16901                 value : 0,
16902                 html : default_text
16903             });
16904             
16905             var o = {};
16906             o[this.valueField] = 0;
16907             o[this.displayField] = default_text;
16908             
16909             this.ios_options.push({
16910                 data : o,
16911                 el : opt
16912             });
16913             
16914         }
16915         
16916         this.store.data.each(function(d, rowIndex){
16917             
16918             var html = '';
16919             
16920             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16921                 html = d.data[this.displayField];
16922             }
16923             
16924             var value = '';
16925             
16926             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16927                 value = d.data[this.valueField];
16928             }
16929             
16930             var option = {
16931                 tag: 'option',
16932                 value : value,
16933                 html : html
16934             };
16935             
16936             if(this.value == d.data[this.valueField]){
16937                 option['selected'] = true;
16938             }
16939             
16940             var opt = this.inputEl().createChild(option);
16941             
16942             this.ios_options.push({
16943                 data : d.data,
16944                 el : opt
16945             });
16946             
16947         }, this);
16948         
16949         this.inputEl().on('change', function(){
16950            this.fireEvent('select', this);
16951         }, this);
16952         
16953     },
16954     
16955     clearIOSView: function()
16956     {
16957         this.inputEl().dom.innerHTML = '';
16958         
16959         this.ios_options = [];
16960     },
16961     
16962     setIOSValue: function(v)
16963     {
16964         this.value = v;
16965         
16966         if(!this.ios_options){
16967             return;
16968         }
16969         
16970         Roo.each(this.ios_options, function(opts){
16971            
16972            opts.el.dom.removeAttribute('selected');
16973            
16974            if(opts.data[this.valueField] != v){
16975                return;
16976            }
16977            
16978            opts.el.dom.setAttribute('selected', true);
16979            
16980         }, this);
16981     }
16982
16983     /** 
16984     * @cfg {Boolean} grow 
16985     * @hide 
16986     */
16987     /** 
16988     * @cfg {Number} growMin 
16989     * @hide 
16990     */
16991     /** 
16992     * @cfg {Number} growMax 
16993     * @hide 
16994     */
16995     /**
16996      * @hide
16997      * @method autoSize
16998      */
16999 });
17000
17001 Roo.apply(Roo.bootstrap.ComboBox,  {
17002     
17003     header : {
17004         tag: 'div',
17005         cls: 'modal-header',
17006         cn: [
17007             {
17008                 tag: 'h4',
17009                 cls: 'modal-title'
17010             }
17011         ]
17012     },
17013     
17014     body : {
17015         tag: 'div',
17016         cls: 'modal-body',
17017         cn: [
17018             {
17019                 tag: 'ul',
17020                 cls: 'list-group'
17021             }
17022         ]
17023     },
17024     
17025     listItemRadio : {
17026         tag: 'li',
17027         cls: 'list-group-item',
17028         cn: [
17029             {
17030                 tag: 'span',
17031                 cls: 'roo-combobox-list-group-item-value'
17032             },
17033             {
17034                 tag: 'div',
17035                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17036                 cn: [
17037                     {
17038                         tag: 'input',
17039                         type: 'radio'
17040                     },
17041                     {
17042                         tag: 'label'
17043                     }
17044                 ]
17045             }
17046         ]
17047     },
17048     
17049     listItemCheckbox : {
17050         tag: 'li',
17051         cls: 'list-group-item',
17052         cn: [
17053             {
17054                 tag: 'span',
17055                 cls: 'roo-combobox-list-group-item-value'
17056             },
17057             {
17058                 tag: 'div',
17059                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17060                 cn: [
17061                     {
17062                         tag: 'input',
17063                         type: 'checkbox'
17064                     },
17065                     {
17066                         tag: 'label'
17067                     }
17068                 ]
17069             }
17070         ]
17071     },
17072     
17073     emptyResult : {
17074         tag: 'div',
17075         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17076     },
17077     
17078     footer : {
17079         tag: 'div',
17080         cls: 'modal-footer',
17081         cn: [
17082             {
17083                 tag: 'div',
17084                 cls: 'row',
17085                 cn: [
17086                     {
17087                         tag: 'div',
17088                         cls: 'col-xs-6 text-left',
17089                         cn: {
17090                             tag: 'button',
17091                             cls: 'btn btn-danger roo-touch-view-cancel',
17092                             html: 'Cancel'
17093                         }
17094                     },
17095                     {
17096                         tag: 'div',
17097                         cls: 'col-xs-6 text-right',
17098                         cn: {
17099                             tag: 'button',
17100                             cls: 'btn btn-success roo-touch-view-ok',
17101                             html: 'OK'
17102                         }
17103                     }
17104                 ]
17105             }
17106         ]
17107         
17108     }
17109 });
17110
17111 Roo.apply(Roo.bootstrap.ComboBox,  {
17112     
17113     touchViewTemplate : {
17114         tag: 'div',
17115         cls: 'modal fade roo-combobox-touch-view',
17116         cn: [
17117             {
17118                 tag: 'div',
17119                 cls: 'modal-dialog',
17120                 style : 'position:fixed', // we have to fix position....
17121                 cn: [
17122                     {
17123                         tag: 'div',
17124                         cls: 'modal-content',
17125                         cn: [
17126                             Roo.bootstrap.ComboBox.header,
17127                             Roo.bootstrap.ComboBox.body,
17128                             Roo.bootstrap.ComboBox.footer
17129                         ]
17130                     }
17131                 ]
17132             }
17133         ]
17134     }
17135 });/*
17136  * Based on:
17137  * Ext JS Library 1.1.1
17138  * Copyright(c) 2006-2007, Ext JS, LLC.
17139  *
17140  * Originally Released Under LGPL - original licence link has changed is not relivant.
17141  *
17142  * Fork - LGPL
17143  * <script type="text/javascript">
17144  */
17145
17146 /**
17147  * @class Roo.View
17148  * @extends Roo.util.Observable
17149  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17150  * This class also supports single and multi selection modes. <br>
17151  * Create a data model bound view:
17152  <pre><code>
17153  var store = new Roo.data.Store(...);
17154
17155  var view = new Roo.View({
17156     el : "my-element",
17157     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17158  
17159     singleSelect: true,
17160     selectedClass: "ydataview-selected",
17161     store: store
17162  });
17163
17164  // listen for node click?
17165  view.on("click", function(vw, index, node, e){
17166  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17167  });
17168
17169  // load XML data
17170  dataModel.load("foobar.xml");
17171  </code></pre>
17172  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17173  * <br><br>
17174  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17175  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17176  * 
17177  * Note: old style constructor is still suported (container, template, config)
17178  * 
17179  * @constructor
17180  * Create a new View
17181  * @param {Object} config The config object
17182  * 
17183  */
17184 Roo.View = function(config, depreciated_tpl, depreciated_config){
17185     
17186     this.parent = false;
17187     
17188     if (typeof(depreciated_tpl) == 'undefined') {
17189         // new way.. - universal constructor.
17190         Roo.apply(this, config);
17191         this.el  = Roo.get(this.el);
17192     } else {
17193         // old format..
17194         this.el  = Roo.get(config);
17195         this.tpl = depreciated_tpl;
17196         Roo.apply(this, depreciated_config);
17197     }
17198     this.wrapEl  = this.el.wrap().wrap();
17199     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17200     
17201     
17202     if(typeof(this.tpl) == "string"){
17203         this.tpl = new Roo.Template(this.tpl);
17204     } else {
17205         // support xtype ctors..
17206         this.tpl = new Roo.factory(this.tpl, Roo);
17207     }
17208     
17209     
17210     this.tpl.compile();
17211     
17212     /** @private */
17213     this.addEvents({
17214         /**
17215          * @event beforeclick
17216          * Fires before a click is processed. Returns false to cancel the default action.
17217          * @param {Roo.View} this
17218          * @param {Number} index The index of the target node
17219          * @param {HTMLElement} node The target node
17220          * @param {Roo.EventObject} e The raw event object
17221          */
17222             "beforeclick" : true,
17223         /**
17224          * @event click
17225          * Fires when a template node is clicked.
17226          * @param {Roo.View} this
17227          * @param {Number} index The index of the target node
17228          * @param {HTMLElement} node The target node
17229          * @param {Roo.EventObject} e The raw event object
17230          */
17231             "click" : true,
17232         /**
17233          * @event dblclick
17234          * Fires when a template node is double clicked.
17235          * @param {Roo.View} this
17236          * @param {Number} index The index of the target node
17237          * @param {HTMLElement} node The target node
17238          * @param {Roo.EventObject} e The raw event object
17239          */
17240             "dblclick" : true,
17241         /**
17242          * @event contextmenu
17243          * Fires when a template node is right clicked.
17244          * @param {Roo.View} this
17245          * @param {Number} index The index of the target node
17246          * @param {HTMLElement} node The target node
17247          * @param {Roo.EventObject} e The raw event object
17248          */
17249             "contextmenu" : true,
17250         /**
17251          * @event selectionchange
17252          * Fires when the selected nodes change.
17253          * @param {Roo.View} this
17254          * @param {Array} selections Array of the selected nodes
17255          */
17256             "selectionchange" : true,
17257     
17258         /**
17259          * @event beforeselect
17260          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17261          * @param {Roo.View} this
17262          * @param {HTMLElement} node The node to be selected
17263          * @param {Array} selections Array of currently selected nodes
17264          */
17265             "beforeselect" : true,
17266         /**
17267          * @event preparedata
17268          * Fires on every row to render, to allow you to change the data.
17269          * @param {Roo.View} this
17270          * @param {Object} data to be rendered (change this)
17271          */
17272           "preparedata" : true
17273           
17274           
17275         });
17276
17277
17278
17279     this.el.on({
17280         "click": this.onClick,
17281         "dblclick": this.onDblClick,
17282         "contextmenu": this.onContextMenu,
17283         scope:this
17284     });
17285
17286     this.selections = [];
17287     this.nodes = [];
17288     this.cmp = new Roo.CompositeElementLite([]);
17289     if(this.store){
17290         this.store = Roo.factory(this.store, Roo.data);
17291         this.setStore(this.store, true);
17292     }
17293     
17294     if ( this.footer && this.footer.xtype) {
17295            
17296          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17297         
17298         this.footer.dataSource = this.store;
17299         this.footer.container = fctr;
17300         this.footer = Roo.factory(this.footer, Roo);
17301         fctr.insertFirst(this.el);
17302         
17303         // this is a bit insane - as the paging toolbar seems to detach the el..
17304 //        dom.parentNode.parentNode.parentNode
17305          // they get detached?
17306     }
17307     
17308     
17309     Roo.View.superclass.constructor.call(this);
17310     
17311     
17312 };
17313
17314 Roo.extend(Roo.View, Roo.util.Observable, {
17315     
17316      /**
17317      * @cfg {Roo.data.Store} store Data store to load data from.
17318      */
17319     store : false,
17320     
17321     /**
17322      * @cfg {String|Roo.Element} el The container element.
17323      */
17324     el : '',
17325     
17326     /**
17327      * @cfg {String|Roo.Template} tpl The template used by this View 
17328      */
17329     tpl : false,
17330     /**
17331      * @cfg {String} dataName the named area of the template to use as the data area
17332      *                          Works with domtemplates roo-name="name"
17333      */
17334     dataName: false,
17335     /**
17336      * @cfg {String} selectedClass The css class to add to selected nodes
17337      */
17338     selectedClass : "x-view-selected",
17339      /**
17340      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17341      */
17342     emptyText : "",
17343     
17344     /**
17345      * @cfg {String} text to display on mask (default Loading)
17346      */
17347     mask : false,
17348     /**
17349      * @cfg {Boolean} multiSelect Allow multiple selection
17350      */
17351     multiSelect : false,
17352     /**
17353      * @cfg {Boolean} singleSelect Allow single selection
17354      */
17355     singleSelect:  false,
17356     
17357     /**
17358      * @cfg {Boolean} toggleSelect - selecting 
17359      */
17360     toggleSelect : false,
17361     
17362     /**
17363      * @cfg {Boolean} tickable - selecting 
17364      */
17365     tickable : false,
17366     
17367     /**
17368      * Returns the element this view is bound to.
17369      * @return {Roo.Element}
17370      */
17371     getEl : function(){
17372         return this.wrapEl;
17373     },
17374     
17375     
17376
17377     /**
17378      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17379      */
17380     refresh : function(){
17381         //Roo.log('refresh');
17382         var t = this.tpl;
17383         
17384         // if we are using something like 'domtemplate', then
17385         // the what gets used is:
17386         // t.applySubtemplate(NAME, data, wrapping data..)
17387         // the outer template then get' applied with
17388         //     the store 'extra data'
17389         // and the body get's added to the
17390         //      roo-name="data" node?
17391         //      <span class='roo-tpl-{name}'></span> ?????
17392         
17393         
17394         
17395         this.clearSelections();
17396         this.el.update("");
17397         var html = [];
17398         var records = this.store.getRange();
17399         if(records.length < 1) {
17400             
17401             // is this valid??  = should it render a template??
17402             
17403             this.el.update(this.emptyText);
17404             return;
17405         }
17406         var el = this.el;
17407         if (this.dataName) {
17408             this.el.update(t.apply(this.store.meta)); //????
17409             el = this.el.child('.roo-tpl-' + this.dataName);
17410         }
17411         
17412         for(var i = 0, len = records.length; i < len; i++){
17413             var data = this.prepareData(records[i].data, i, records[i]);
17414             this.fireEvent("preparedata", this, data, i, records[i]);
17415             
17416             var d = Roo.apply({}, data);
17417             
17418             if(this.tickable){
17419                 Roo.apply(d, {'roo-id' : Roo.id()});
17420                 
17421                 var _this = this;
17422             
17423                 Roo.each(this.parent.item, function(item){
17424                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17425                         return;
17426                     }
17427                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17428                 });
17429             }
17430             
17431             html[html.length] = Roo.util.Format.trim(
17432                 this.dataName ?
17433                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17434                     t.apply(d)
17435             );
17436         }
17437         
17438         
17439         
17440         el.update(html.join(""));
17441         this.nodes = el.dom.childNodes;
17442         this.updateIndexes(0);
17443     },
17444     
17445
17446     /**
17447      * Function to override to reformat the data that is sent to
17448      * the template for each node.
17449      * DEPRICATED - use the preparedata event handler.
17450      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17451      * a JSON object for an UpdateManager bound view).
17452      */
17453     prepareData : function(data, index, record)
17454     {
17455         this.fireEvent("preparedata", this, data, index, record);
17456         return data;
17457     },
17458
17459     onUpdate : function(ds, record){
17460         // Roo.log('on update');   
17461         this.clearSelections();
17462         var index = this.store.indexOf(record);
17463         var n = this.nodes[index];
17464         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17465         n.parentNode.removeChild(n);
17466         this.updateIndexes(index, index);
17467     },
17468
17469     
17470     
17471 // --------- FIXME     
17472     onAdd : function(ds, records, index)
17473     {
17474         //Roo.log(['on Add', ds, records, index] );        
17475         this.clearSelections();
17476         if(this.nodes.length == 0){
17477             this.refresh();
17478             return;
17479         }
17480         var n = this.nodes[index];
17481         for(var i = 0, len = records.length; i < len; i++){
17482             var d = this.prepareData(records[i].data, i, records[i]);
17483             if(n){
17484                 this.tpl.insertBefore(n, d);
17485             }else{
17486                 
17487                 this.tpl.append(this.el, d);
17488             }
17489         }
17490         this.updateIndexes(index);
17491     },
17492
17493     onRemove : function(ds, record, index){
17494        // Roo.log('onRemove');
17495         this.clearSelections();
17496         var el = this.dataName  ?
17497             this.el.child('.roo-tpl-' + this.dataName) :
17498             this.el; 
17499         
17500         el.dom.removeChild(this.nodes[index]);
17501         this.updateIndexes(index);
17502     },
17503
17504     /**
17505      * Refresh an individual node.
17506      * @param {Number} index
17507      */
17508     refreshNode : function(index){
17509         this.onUpdate(this.store, this.store.getAt(index));
17510     },
17511
17512     updateIndexes : function(startIndex, endIndex){
17513         var ns = this.nodes;
17514         startIndex = startIndex || 0;
17515         endIndex = endIndex || ns.length - 1;
17516         for(var i = startIndex; i <= endIndex; i++){
17517             ns[i].nodeIndex = i;
17518         }
17519     },
17520
17521     /**
17522      * Changes the data store this view uses and refresh the view.
17523      * @param {Store} store
17524      */
17525     setStore : function(store, initial){
17526         if(!initial && this.store){
17527             this.store.un("datachanged", this.refresh);
17528             this.store.un("add", this.onAdd);
17529             this.store.un("remove", this.onRemove);
17530             this.store.un("update", this.onUpdate);
17531             this.store.un("clear", this.refresh);
17532             this.store.un("beforeload", this.onBeforeLoad);
17533             this.store.un("load", this.onLoad);
17534             this.store.un("loadexception", this.onLoad);
17535         }
17536         if(store){
17537           
17538             store.on("datachanged", this.refresh, this);
17539             store.on("add", this.onAdd, this);
17540             store.on("remove", this.onRemove, this);
17541             store.on("update", this.onUpdate, this);
17542             store.on("clear", this.refresh, this);
17543             store.on("beforeload", this.onBeforeLoad, this);
17544             store.on("load", this.onLoad, this);
17545             store.on("loadexception", this.onLoad, this);
17546         }
17547         
17548         if(store){
17549             this.refresh();
17550         }
17551     },
17552     /**
17553      * onbeforeLoad - masks the loading area.
17554      *
17555      */
17556     onBeforeLoad : function(store,opts)
17557     {
17558          //Roo.log('onBeforeLoad');   
17559         if (!opts.add) {
17560             this.el.update("");
17561         }
17562         this.el.mask(this.mask ? this.mask : "Loading" ); 
17563     },
17564     onLoad : function ()
17565     {
17566         this.el.unmask();
17567     },
17568     
17569
17570     /**
17571      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17572      * @param {HTMLElement} node
17573      * @return {HTMLElement} The template node
17574      */
17575     findItemFromChild : function(node){
17576         var el = this.dataName  ?
17577             this.el.child('.roo-tpl-' + this.dataName,true) :
17578             this.el.dom; 
17579         
17580         if(!node || node.parentNode == el){
17581                     return node;
17582             }
17583             var p = node.parentNode;
17584             while(p && p != el){
17585             if(p.parentNode == el){
17586                 return p;
17587             }
17588             p = p.parentNode;
17589         }
17590             return null;
17591     },
17592
17593     /** @ignore */
17594     onClick : function(e){
17595         var item = this.findItemFromChild(e.getTarget());
17596         if(item){
17597             var index = this.indexOf(item);
17598             if(this.onItemClick(item, index, e) !== false){
17599                 this.fireEvent("click", this, index, item, e);
17600             }
17601         }else{
17602             this.clearSelections();
17603         }
17604     },
17605
17606     /** @ignore */
17607     onContextMenu : function(e){
17608         var item = this.findItemFromChild(e.getTarget());
17609         if(item){
17610             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17611         }
17612     },
17613
17614     /** @ignore */
17615     onDblClick : function(e){
17616         var item = this.findItemFromChild(e.getTarget());
17617         if(item){
17618             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17619         }
17620     },
17621
17622     onItemClick : function(item, index, e)
17623     {
17624         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17625             return false;
17626         }
17627         if (this.toggleSelect) {
17628             var m = this.isSelected(item) ? 'unselect' : 'select';
17629             //Roo.log(m);
17630             var _t = this;
17631             _t[m](item, true, false);
17632             return true;
17633         }
17634         if(this.multiSelect || this.singleSelect){
17635             if(this.multiSelect && e.shiftKey && this.lastSelection){
17636                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17637             }else{
17638                 this.select(item, this.multiSelect && e.ctrlKey);
17639                 this.lastSelection = item;
17640             }
17641             
17642             if(!this.tickable){
17643                 e.preventDefault();
17644             }
17645             
17646         }
17647         return true;
17648     },
17649
17650     /**
17651      * Get the number of selected nodes.
17652      * @return {Number}
17653      */
17654     getSelectionCount : function(){
17655         return this.selections.length;
17656     },
17657
17658     /**
17659      * Get the currently selected nodes.
17660      * @return {Array} An array of HTMLElements
17661      */
17662     getSelectedNodes : function(){
17663         return this.selections;
17664     },
17665
17666     /**
17667      * Get the indexes of the selected nodes.
17668      * @return {Array}
17669      */
17670     getSelectedIndexes : function(){
17671         var indexes = [], s = this.selections;
17672         for(var i = 0, len = s.length; i < len; i++){
17673             indexes.push(s[i].nodeIndex);
17674         }
17675         return indexes;
17676     },
17677
17678     /**
17679      * Clear all selections
17680      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17681      */
17682     clearSelections : function(suppressEvent){
17683         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17684             this.cmp.elements = this.selections;
17685             this.cmp.removeClass(this.selectedClass);
17686             this.selections = [];
17687             if(!suppressEvent){
17688                 this.fireEvent("selectionchange", this, this.selections);
17689             }
17690         }
17691     },
17692
17693     /**
17694      * Returns true if the passed node is selected
17695      * @param {HTMLElement/Number} node The node or node index
17696      * @return {Boolean}
17697      */
17698     isSelected : function(node){
17699         var s = this.selections;
17700         if(s.length < 1){
17701             return false;
17702         }
17703         node = this.getNode(node);
17704         return s.indexOf(node) !== -1;
17705     },
17706
17707     /**
17708      * Selects nodes.
17709      * @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
17710      * @param {Boolean} keepExisting (optional) true to keep existing selections
17711      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17712      */
17713     select : function(nodeInfo, keepExisting, suppressEvent){
17714         if(nodeInfo instanceof Array){
17715             if(!keepExisting){
17716                 this.clearSelections(true);
17717             }
17718             for(var i = 0, len = nodeInfo.length; i < len; i++){
17719                 this.select(nodeInfo[i], true, true);
17720             }
17721             return;
17722         } 
17723         var node = this.getNode(nodeInfo);
17724         if(!node || this.isSelected(node)){
17725             return; // already selected.
17726         }
17727         if(!keepExisting){
17728             this.clearSelections(true);
17729         }
17730         
17731         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17732             Roo.fly(node).addClass(this.selectedClass);
17733             this.selections.push(node);
17734             if(!suppressEvent){
17735                 this.fireEvent("selectionchange", this, this.selections);
17736             }
17737         }
17738         
17739         
17740     },
17741       /**
17742      * Unselects nodes.
17743      * @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
17744      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17745      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17746      */
17747     unselect : function(nodeInfo, keepExisting, suppressEvent)
17748     {
17749         if(nodeInfo instanceof Array){
17750             Roo.each(this.selections, function(s) {
17751                 this.unselect(s, nodeInfo);
17752             }, this);
17753             return;
17754         }
17755         var node = this.getNode(nodeInfo);
17756         if(!node || !this.isSelected(node)){
17757             //Roo.log("not selected");
17758             return; // not selected.
17759         }
17760         // fireevent???
17761         var ns = [];
17762         Roo.each(this.selections, function(s) {
17763             if (s == node ) {
17764                 Roo.fly(node).removeClass(this.selectedClass);
17765
17766                 return;
17767             }
17768             ns.push(s);
17769         },this);
17770         
17771         this.selections= ns;
17772         this.fireEvent("selectionchange", this, this.selections);
17773     },
17774
17775     /**
17776      * Gets a template node.
17777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17778      * @return {HTMLElement} The node or null if it wasn't found
17779      */
17780     getNode : function(nodeInfo){
17781         if(typeof nodeInfo == "string"){
17782             return document.getElementById(nodeInfo);
17783         }else if(typeof nodeInfo == "number"){
17784             return this.nodes[nodeInfo];
17785         }
17786         return nodeInfo;
17787     },
17788
17789     /**
17790      * Gets a range template nodes.
17791      * @param {Number} startIndex
17792      * @param {Number} endIndex
17793      * @return {Array} An array of nodes
17794      */
17795     getNodes : function(start, end){
17796         var ns = this.nodes;
17797         start = start || 0;
17798         end = typeof end == "undefined" ? ns.length - 1 : end;
17799         var nodes = [];
17800         if(start <= end){
17801             for(var i = start; i <= end; i++){
17802                 nodes.push(ns[i]);
17803             }
17804         } else{
17805             for(var i = start; i >= end; i--){
17806                 nodes.push(ns[i]);
17807             }
17808         }
17809         return nodes;
17810     },
17811
17812     /**
17813      * Finds the index of the passed node
17814      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17815      * @return {Number} The index of the node or -1
17816      */
17817     indexOf : function(node){
17818         node = this.getNode(node);
17819         if(typeof node.nodeIndex == "number"){
17820             return node.nodeIndex;
17821         }
17822         var ns = this.nodes;
17823         for(var i = 0, len = ns.length; i < len; i++){
17824             if(ns[i] == node){
17825                 return i;
17826             }
17827         }
17828         return -1;
17829     }
17830 });
17831 /*
17832  * - LGPL
17833  *
17834  * based on jquery fullcalendar
17835  * 
17836  */
17837
17838 Roo.bootstrap = Roo.bootstrap || {};
17839 /**
17840  * @class Roo.bootstrap.Calendar
17841  * @extends Roo.bootstrap.Component
17842  * Bootstrap Calendar class
17843  * @cfg {Boolean} loadMask (true|false) default false
17844  * @cfg {Object} header generate the user specific header of the calendar, default false
17845
17846  * @constructor
17847  * Create a new Container
17848  * @param {Object} config The config object
17849  */
17850
17851
17852
17853 Roo.bootstrap.Calendar = function(config){
17854     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17855      this.addEvents({
17856         /**
17857              * @event select
17858              * Fires when a date is selected
17859              * @param {DatePicker} this
17860              * @param {Date} date The selected date
17861              */
17862         'select': true,
17863         /**
17864              * @event monthchange
17865              * Fires when the displayed month changes 
17866              * @param {DatePicker} this
17867              * @param {Date} date The selected month
17868              */
17869         'monthchange': true,
17870         /**
17871              * @event evententer
17872              * Fires when mouse over an event
17873              * @param {Calendar} this
17874              * @param {event} Event
17875              */
17876         'evententer': true,
17877         /**
17878              * @event eventleave
17879              * Fires when the mouse leaves an
17880              * @param {Calendar} this
17881              * @param {event}
17882              */
17883         'eventleave': true,
17884         /**
17885              * @event eventclick
17886              * Fires when the mouse click an
17887              * @param {Calendar} this
17888              * @param {event}
17889              */
17890         'eventclick': true
17891         
17892     });
17893
17894 };
17895
17896 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17897     
17898      /**
17899      * @cfg {Number} startDay
17900      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17901      */
17902     startDay : 0,
17903     
17904     loadMask : false,
17905     
17906     header : false,
17907       
17908     getAutoCreate : function(){
17909         
17910         
17911         var fc_button = function(name, corner, style, content ) {
17912             return Roo.apply({},{
17913                 tag : 'span',
17914                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17915                          (corner.length ?
17916                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17917                             ''
17918                         ),
17919                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17920                 unselectable: 'on'
17921             });
17922         };
17923         
17924         var header = {};
17925         
17926         if(!this.header){
17927             header = {
17928                 tag : 'table',
17929                 cls : 'fc-header',
17930                 style : 'width:100%',
17931                 cn : [
17932                     {
17933                         tag: 'tr',
17934                         cn : [
17935                             {
17936                                 tag : 'td',
17937                                 cls : 'fc-header-left',
17938                                 cn : [
17939                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17940                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17941                                     { tag: 'span', cls: 'fc-header-space' },
17942                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17943
17944
17945                                 ]
17946                             },
17947
17948                             {
17949                                 tag : 'td',
17950                                 cls : 'fc-header-center',
17951                                 cn : [
17952                                     {
17953                                         tag: 'span',
17954                                         cls: 'fc-header-title',
17955                                         cn : {
17956                                             tag: 'H2',
17957                                             html : 'month / year'
17958                                         }
17959                                     }
17960
17961                                 ]
17962                             },
17963                             {
17964                                 tag : 'td',
17965                                 cls : 'fc-header-right',
17966                                 cn : [
17967                               /*      fc_button('month', 'left', '', 'month' ),
17968                                     fc_button('week', '', '', 'week' ),
17969                                     fc_button('day', 'right', '', 'day' )
17970                                 */    
17971
17972                                 ]
17973                             }
17974
17975                         ]
17976                     }
17977                 ]
17978             };
17979         }
17980         
17981         header = this.header;
17982         
17983        
17984         var cal_heads = function() {
17985             var ret = [];
17986             // fixme - handle this.
17987             
17988             for (var i =0; i < Date.dayNames.length; i++) {
17989                 var d = Date.dayNames[i];
17990                 ret.push({
17991                     tag: 'th',
17992                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17993                     html : d.substring(0,3)
17994                 });
17995                 
17996             }
17997             ret[0].cls += ' fc-first';
17998             ret[6].cls += ' fc-last';
17999             return ret;
18000         };
18001         var cal_cell = function(n) {
18002             return  {
18003                 tag: 'td',
18004                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18005                 cn : [
18006                     {
18007                         cn : [
18008                             {
18009                                 cls: 'fc-day-number',
18010                                 html: 'D'
18011                             },
18012                             {
18013                                 cls: 'fc-day-content',
18014                              
18015                                 cn : [
18016                                      {
18017                                         style: 'position: relative;' // height: 17px;
18018                                     }
18019                                 ]
18020                             }
18021                             
18022                             
18023                         ]
18024                     }
18025                 ]
18026                 
18027             }
18028         };
18029         var cal_rows = function() {
18030             
18031             var ret = [];
18032             for (var r = 0; r < 6; r++) {
18033                 var row= {
18034                     tag : 'tr',
18035                     cls : 'fc-week',
18036                     cn : []
18037                 };
18038                 
18039                 for (var i =0; i < Date.dayNames.length; i++) {
18040                     var d = Date.dayNames[i];
18041                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18042
18043                 }
18044                 row.cn[0].cls+=' fc-first';
18045                 row.cn[0].cn[0].style = 'min-height:90px';
18046                 row.cn[6].cls+=' fc-last';
18047                 ret.push(row);
18048                 
18049             }
18050             ret[0].cls += ' fc-first';
18051             ret[4].cls += ' fc-prev-last';
18052             ret[5].cls += ' fc-last';
18053             return ret;
18054             
18055         };
18056         
18057         var cal_table = {
18058             tag: 'table',
18059             cls: 'fc-border-separate',
18060             style : 'width:100%',
18061             cellspacing  : 0,
18062             cn : [
18063                 { 
18064                     tag: 'thead',
18065                     cn : [
18066                         { 
18067                             tag: 'tr',
18068                             cls : 'fc-first fc-last',
18069                             cn : cal_heads()
18070                         }
18071                     ]
18072                 },
18073                 { 
18074                     tag: 'tbody',
18075                     cn : cal_rows()
18076                 }
18077                   
18078             ]
18079         };
18080          
18081          var cfg = {
18082             cls : 'fc fc-ltr',
18083             cn : [
18084                 header,
18085                 {
18086                     cls : 'fc-content',
18087                     style : "position: relative;",
18088                     cn : [
18089                         {
18090                             cls : 'fc-view fc-view-month fc-grid',
18091                             style : 'position: relative',
18092                             unselectable : 'on',
18093                             cn : [
18094                                 {
18095                                     cls : 'fc-event-container',
18096                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18097                                 },
18098                                 cal_table
18099                             ]
18100                         }
18101                     ]
18102     
18103                 }
18104            ] 
18105             
18106         };
18107         
18108          
18109         
18110         return cfg;
18111     },
18112     
18113     
18114     initEvents : function()
18115     {
18116         if(!this.store){
18117             throw "can not find store for calendar";
18118         }
18119         
18120         var mark = {
18121             tag: "div",
18122             cls:"x-dlg-mask",
18123             style: "text-align:center",
18124             cn: [
18125                 {
18126                     tag: "div",
18127                     style: "background-color:white;width:50%;margin:250 auto",
18128                     cn: [
18129                         {
18130                             tag: "img",
18131                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18132                         },
18133                         {
18134                             tag: "span",
18135                             html: "Loading"
18136                         }
18137                         
18138                     ]
18139                 }
18140             ]
18141         };
18142         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18143         
18144         var size = this.el.select('.fc-content', true).first().getSize();
18145         this.maskEl.setSize(size.width, size.height);
18146         this.maskEl.enableDisplayMode("block");
18147         if(!this.loadMask){
18148             this.maskEl.hide();
18149         }
18150         
18151         this.store = Roo.factory(this.store, Roo.data);
18152         this.store.on('load', this.onLoad, this);
18153         this.store.on('beforeload', this.onBeforeLoad, this);
18154         
18155         this.resize();
18156         
18157         this.cells = this.el.select('.fc-day',true);
18158         //Roo.log(this.cells);
18159         this.textNodes = this.el.query('.fc-day-number');
18160         this.cells.addClassOnOver('fc-state-hover');
18161         
18162         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18163         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18164         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18165         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18166         
18167         this.on('monthchange', this.onMonthChange, this);
18168         
18169         this.update(new Date().clearTime());
18170     },
18171     
18172     resize : function() {
18173         var sz  = this.el.getSize();
18174         
18175         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18176         this.el.select('.fc-day-content div',true).setHeight(34);
18177     },
18178     
18179     
18180     // private
18181     showPrevMonth : function(e){
18182         this.update(this.activeDate.add("mo", -1));
18183     },
18184     showToday : function(e){
18185         this.update(new Date().clearTime());
18186     },
18187     // private
18188     showNextMonth : function(e){
18189         this.update(this.activeDate.add("mo", 1));
18190     },
18191
18192     // private
18193     showPrevYear : function(){
18194         this.update(this.activeDate.add("y", -1));
18195     },
18196
18197     // private
18198     showNextYear : function(){
18199         this.update(this.activeDate.add("y", 1));
18200     },
18201
18202     
18203    // private
18204     update : function(date)
18205     {
18206         var vd = this.activeDate;
18207         this.activeDate = date;
18208 //        if(vd && this.el){
18209 //            var t = date.getTime();
18210 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18211 //                Roo.log('using add remove');
18212 //                
18213 //                this.fireEvent('monthchange', this, date);
18214 //                
18215 //                this.cells.removeClass("fc-state-highlight");
18216 //                this.cells.each(function(c){
18217 //                   if(c.dateValue == t){
18218 //                       c.addClass("fc-state-highlight");
18219 //                       setTimeout(function(){
18220 //                            try{c.dom.firstChild.focus();}catch(e){}
18221 //                       }, 50);
18222 //                       return false;
18223 //                   }
18224 //                   return true;
18225 //                });
18226 //                return;
18227 //            }
18228 //        }
18229         
18230         var days = date.getDaysInMonth();
18231         
18232         var firstOfMonth = date.getFirstDateOfMonth();
18233         var startingPos = firstOfMonth.getDay()-this.startDay;
18234         
18235         if(startingPos < this.startDay){
18236             startingPos += 7;
18237         }
18238         
18239         var pm = date.add(Date.MONTH, -1);
18240         var prevStart = pm.getDaysInMonth()-startingPos;
18241 //        
18242         this.cells = this.el.select('.fc-day',true);
18243         this.textNodes = this.el.query('.fc-day-number');
18244         this.cells.addClassOnOver('fc-state-hover');
18245         
18246         var cells = this.cells.elements;
18247         var textEls = this.textNodes;
18248         
18249         Roo.each(cells, function(cell){
18250             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18251         });
18252         
18253         days += startingPos;
18254
18255         // convert everything to numbers so it's fast
18256         var day = 86400000;
18257         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18258         //Roo.log(d);
18259         //Roo.log(pm);
18260         //Roo.log(prevStart);
18261         
18262         var today = new Date().clearTime().getTime();
18263         var sel = date.clearTime().getTime();
18264         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18265         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18266         var ddMatch = this.disabledDatesRE;
18267         var ddText = this.disabledDatesText;
18268         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18269         var ddaysText = this.disabledDaysText;
18270         var format = this.format;
18271         
18272         var setCellClass = function(cal, cell){
18273             cell.row = 0;
18274             cell.events = [];
18275             cell.more = [];
18276             //Roo.log('set Cell Class');
18277             cell.title = "";
18278             var t = d.getTime();
18279             
18280             //Roo.log(d);
18281             
18282             cell.dateValue = t;
18283             if(t == today){
18284                 cell.className += " fc-today";
18285                 cell.className += " fc-state-highlight";
18286                 cell.title = cal.todayText;
18287             }
18288             if(t == sel){
18289                 // disable highlight in other month..
18290                 //cell.className += " fc-state-highlight";
18291                 
18292             }
18293             // disabling
18294             if(t < min) {
18295                 cell.className = " fc-state-disabled";
18296                 cell.title = cal.minText;
18297                 return;
18298             }
18299             if(t > max) {
18300                 cell.className = " fc-state-disabled";
18301                 cell.title = cal.maxText;
18302                 return;
18303             }
18304             if(ddays){
18305                 if(ddays.indexOf(d.getDay()) != -1){
18306                     cell.title = ddaysText;
18307                     cell.className = " fc-state-disabled";
18308                 }
18309             }
18310             if(ddMatch && format){
18311                 var fvalue = d.dateFormat(format);
18312                 if(ddMatch.test(fvalue)){
18313                     cell.title = ddText.replace("%0", fvalue);
18314                     cell.className = " fc-state-disabled";
18315                 }
18316             }
18317             
18318             if (!cell.initialClassName) {
18319                 cell.initialClassName = cell.dom.className;
18320             }
18321             
18322             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18323         };
18324
18325         var i = 0;
18326         
18327         for(; i < startingPos; i++) {
18328             textEls[i].innerHTML = (++prevStart);
18329             d.setDate(d.getDate()+1);
18330             
18331             cells[i].className = "fc-past fc-other-month";
18332             setCellClass(this, cells[i]);
18333         }
18334         
18335         var intDay = 0;
18336         
18337         for(; i < days; i++){
18338             intDay = i - startingPos + 1;
18339             textEls[i].innerHTML = (intDay);
18340             d.setDate(d.getDate()+1);
18341             
18342             cells[i].className = ''; // "x-date-active";
18343             setCellClass(this, cells[i]);
18344         }
18345         var extraDays = 0;
18346         
18347         for(; i < 42; i++) {
18348             textEls[i].innerHTML = (++extraDays);
18349             d.setDate(d.getDate()+1);
18350             
18351             cells[i].className = "fc-future fc-other-month";
18352             setCellClass(this, cells[i]);
18353         }
18354         
18355         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18356         
18357         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18358         
18359         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18360         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18361         
18362         if(totalRows != 6){
18363             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18364             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18365         }
18366         
18367         this.fireEvent('monthchange', this, date);
18368         
18369         
18370         /*
18371         if(!this.internalRender){
18372             var main = this.el.dom.firstChild;
18373             var w = main.offsetWidth;
18374             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18375             Roo.fly(main).setWidth(w);
18376             this.internalRender = true;
18377             // opera does not respect the auto grow header center column
18378             // then, after it gets a width opera refuses to recalculate
18379             // without a second pass
18380             if(Roo.isOpera && !this.secondPass){
18381                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18382                 this.secondPass = true;
18383                 this.update.defer(10, this, [date]);
18384             }
18385         }
18386         */
18387         
18388     },
18389     
18390     findCell : function(dt) {
18391         dt = dt.clearTime().getTime();
18392         var ret = false;
18393         this.cells.each(function(c){
18394             //Roo.log("check " +c.dateValue + '?=' + dt);
18395             if(c.dateValue == dt){
18396                 ret = c;
18397                 return false;
18398             }
18399             return true;
18400         });
18401         
18402         return ret;
18403     },
18404     
18405     findCells : function(ev) {
18406         var s = ev.start.clone().clearTime().getTime();
18407        // Roo.log(s);
18408         var e= ev.end.clone().clearTime().getTime();
18409        // Roo.log(e);
18410         var ret = [];
18411         this.cells.each(function(c){
18412              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18413             
18414             if(c.dateValue > e){
18415                 return ;
18416             }
18417             if(c.dateValue < s){
18418                 return ;
18419             }
18420             ret.push(c);
18421         });
18422         
18423         return ret;    
18424     },
18425     
18426 //    findBestRow: function(cells)
18427 //    {
18428 //        var ret = 0;
18429 //        
18430 //        for (var i =0 ; i < cells.length;i++) {
18431 //            ret  = Math.max(cells[i].rows || 0,ret);
18432 //        }
18433 //        return ret;
18434 //        
18435 //    },
18436     
18437     
18438     addItem : function(ev)
18439     {
18440         // look for vertical location slot in
18441         var cells = this.findCells(ev);
18442         
18443 //        ev.row = this.findBestRow(cells);
18444         
18445         // work out the location.
18446         
18447         var crow = false;
18448         var rows = [];
18449         for(var i =0; i < cells.length; i++) {
18450             
18451             cells[i].row = cells[0].row;
18452             
18453             if(i == 0){
18454                 cells[i].row = cells[i].row + 1;
18455             }
18456             
18457             if (!crow) {
18458                 crow = {
18459                     start : cells[i],
18460                     end :  cells[i]
18461                 };
18462                 continue;
18463             }
18464             if (crow.start.getY() == cells[i].getY()) {
18465                 // on same row.
18466                 crow.end = cells[i];
18467                 continue;
18468             }
18469             // different row.
18470             rows.push(crow);
18471             crow = {
18472                 start: cells[i],
18473                 end : cells[i]
18474             };
18475             
18476         }
18477         
18478         rows.push(crow);
18479         ev.els = [];
18480         ev.rows = rows;
18481         ev.cells = cells;
18482         
18483         cells[0].events.push(ev);
18484         
18485         this.calevents.push(ev);
18486     },
18487     
18488     clearEvents: function() {
18489         
18490         if(!this.calevents){
18491             return;
18492         }
18493         
18494         Roo.each(this.cells.elements, function(c){
18495             c.row = 0;
18496             c.events = [];
18497             c.more = [];
18498         });
18499         
18500         Roo.each(this.calevents, function(e) {
18501             Roo.each(e.els, function(el) {
18502                 el.un('mouseenter' ,this.onEventEnter, this);
18503                 el.un('mouseleave' ,this.onEventLeave, this);
18504                 el.remove();
18505             },this);
18506         },this);
18507         
18508         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18509             e.remove();
18510         });
18511         
18512     },
18513     
18514     renderEvents: function()
18515     {   
18516         var _this = this;
18517         
18518         this.cells.each(function(c) {
18519             
18520             if(c.row < 5){
18521                 return;
18522             }
18523             
18524             var ev = c.events;
18525             
18526             var r = 4;
18527             if(c.row != c.events.length){
18528                 r = 4 - (4 - (c.row - c.events.length));
18529             }
18530             
18531             c.events = ev.slice(0, r);
18532             c.more = ev.slice(r);
18533             
18534             if(c.more.length && c.more.length == 1){
18535                 c.events.push(c.more.pop());
18536             }
18537             
18538             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18539             
18540         });
18541             
18542         this.cells.each(function(c) {
18543             
18544             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18545             
18546             
18547             for (var e = 0; e < c.events.length; e++){
18548                 var ev = c.events[e];
18549                 var rows = ev.rows;
18550                 
18551                 for(var i = 0; i < rows.length; i++) {
18552                 
18553                     // how many rows should it span..
18554
18555                     var  cfg = {
18556                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18557                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18558
18559                         unselectable : "on",
18560                         cn : [
18561                             {
18562                                 cls: 'fc-event-inner',
18563                                 cn : [
18564     //                                {
18565     //                                  tag:'span',
18566     //                                  cls: 'fc-event-time',
18567     //                                  html : cells.length > 1 ? '' : ev.time
18568     //                                },
18569                                     {
18570                                       tag:'span',
18571                                       cls: 'fc-event-title',
18572                                       html : String.format('{0}', ev.title)
18573                                     }
18574
18575
18576                                 ]
18577                             },
18578                             {
18579                                 cls: 'ui-resizable-handle ui-resizable-e',
18580                                 html : '&nbsp;&nbsp;&nbsp'
18581                             }
18582
18583                         ]
18584                     };
18585
18586                     if (i == 0) {
18587                         cfg.cls += ' fc-event-start';
18588                     }
18589                     if ((i+1) == rows.length) {
18590                         cfg.cls += ' fc-event-end';
18591                     }
18592
18593                     var ctr = _this.el.select('.fc-event-container',true).first();
18594                     var cg = ctr.createChild(cfg);
18595
18596                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18597                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18598
18599                     var r = (c.more.length) ? 1 : 0;
18600                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18601                     cg.setWidth(ebox.right - sbox.x -2);
18602
18603                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18604                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18605                     cg.on('click', _this.onEventClick, _this, ev);
18606
18607                     ev.els.push(cg);
18608                     
18609                 }
18610                 
18611             }
18612             
18613             
18614             if(c.more.length){
18615                 var  cfg = {
18616                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18617                     style : 'position: absolute',
18618                     unselectable : "on",
18619                     cn : [
18620                         {
18621                             cls: 'fc-event-inner',
18622                             cn : [
18623                                 {
18624                                   tag:'span',
18625                                   cls: 'fc-event-title',
18626                                   html : 'More'
18627                                 }
18628
18629
18630                             ]
18631                         },
18632                         {
18633                             cls: 'ui-resizable-handle ui-resizable-e',
18634                             html : '&nbsp;&nbsp;&nbsp'
18635                         }
18636
18637                     ]
18638                 };
18639
18640                 var ctr = _this.el.select('.fc-event-container',true).first();
18641                 var cg = ctr.createChild(cfg);
18642
18643                 var sbox = c.select('.fc-day-content',true).first().getBox();
18644                 var ebox = c.select('.fc-day-content',true).first().getBox();
18645                 //Roo.log(cg);
18646                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18647                 cg.setWidth(ebox.right - sbox.x -2);
18648
18649                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18650                 
18651             }
18652             
18653         });
18654         
18655         
18656         
18657     },
18658     
18659     onEventEnter: function (e, el,event,d) {
18660         this.fireEvent('evententer', this, el, event);
18661     },
18662     
18663     onEventLeave: function (e, el,event,d) {
18664         this.fireEvent('eventleave', this, el, event);
18665     },
18666     
18667     onEventClick: function (e, el,event,d) {
18668         this.fireEvent('eventclick', this, el, event);
18669     },
18670     
18671     onMonthChange: function () {
18672         this.store.load();
18673     },
18674     
18675     onMoreEventClick: function(e, el, more)
18676     {
18677         var _this = this;
18678         
18679         this.calpopover.placement = 'right';
18680         this.calpopover.setTitle('More');
18681         
18682         this.calpopover.setContent('');
18683         
18684         var ctr = this.calpopover.el.select('.popover-content', true).first();
18685         
18686         Roo.each(more, function(m){
18687             var cfg = {
18688                 cls : 'fc-event-hori fc-event-draggable',
18689                 html : m.title
18690             };
18691             var cg = ctr.createChild(cfg);
18692             
18693             cg.on('click', _this.onEventClick, _this, m);
18694         });
18695         
18696         this.calpopover.show(el);
18697         
18698         
18699     },
18700     
18701     onLoad: function () 
18702     {   
18703         this.calevents = [];
18704         var cal = this;
18705         
18706         if(this.store.getCount() > 0){
18707             this.store.data.each(function(d){
18708                cal.addItem({
18709                     id : d.data.id,
18710                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18711                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18712                     time : d.data.start_time,
18713                     title : d.data.title,
18714                     description : d.data.description,
18715                     venue : d.data.venue
18716                 });
18717             });
18718         }
18719         
18720         this.renderEvents();
18721         
18722         if(this.calevents.length && this.loadMask){
18723             this.maskEl.hide();
18724         }
18725     },
18726     
18727     onBeforeLoad: function()
18728     {
18729         this.clearEvents();
18730         if(this.loadMask){
18731             this.maskEl.show();
18732         }
18733     }
18734 });
18735
18736  
18737  /*
18738  * - LGPL
18739  *
18740  * element
18741  * 
18742  */
18743
18744 /**
18745  * @class Roo.bootstrap.Popover
18746  * @extends Roo.bootstrap.Component
18747  * Bootstrap Popover class
18748  * @cfg {String} html contents of the popover   (or false to use children..)
18749  * @cfg {String} title of popover (or false to hide)
18750  * @cfg {String} placement how it is placed
18751  * @cfg {String} trigger click || hover (or false to trigger manually)
18752  * @cfg {String} over what (parent or false to trigger manually.)
18753  * @cfg {Number} delay - delay before showing
18754  
18755  * @constructor
18756  * Create a new Popover
18757  * @param {Object} config The config object
18758  */
18759
18760 Roo.bootstrap.Popover = function(config){
18761     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18762     
18763     this.addEvents({
18764         // raw events
18765          /**
18766          * @event show
18767          * After the popover show
18768          * 
18769          * @param {Roo.bootstrap.Popover} this
18770          */
18771         "show" : true,
18772         /**
18773          * @event hide
18774          * After the popover hide
18775          * 
18776          * @param {Roo.bootstrap.Popover} this
18777          */
18778         "hide" : true
18779     });
18780 };
18781
18782 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18783     
18784     title: 'Fill in a title',
18785     html: false,
18786     
18787     placement : 'right',
18788     trigger : 'hover', // hover
18789     
18790     delay : 0,
18791     
18792     over: 'parent',
18793     
18794     can_build_overlaid : false,
18795     
18796     getChildContainer : function()
18797     {
18798         return this.el.select('.popover-content',true).first();
18799     },
18800     
18801     getAutoCreate : function(){
18802          
18803         var cfg = {
18804            cls : 'popover roo-dynamic',
18805            style: 'display:block',
18806            cn : [
18807                 {
18808                     cls : 'arrow'
18809                 },
18810                 {
18811                     cls : 'popover-inner',
18812                     cn : [
18813                         {
18814                             tag: 'h3',
18815                             cls: 'popover-title popover-header',
18816                             html : this.title
18817                         },
18818                         {
18819                             cls : 'popover-content popover-body',
18820                             html : this.html
18821                         }
18822                     ]
18823                     
18824                 }
18825            ]
18826         };
18827         
18828         return cfg;
18829     },
18830     setTitle: function(str)
18831     {
18832         this.title = str;
18833         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18834     },
18835     setContent: function(str)
18836     {
18837         this.html = str;
18838         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18839     },
18840     // as it get's added to the bottom of the page.
18841     onRender : function(ct, position)
18842     {
18843         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18844         if(!this.el){
18845             var cfg = Roo.apply({},  this.getAutoCreate());
18846             cfg.id = Roo.id();
18847             
18848             if (this.cls) {
18849                 cfg.cls += ' ' + this.cls;
18850             }
18851             if (this.style) {
18852                 cfg.style = this.style;
18853             }
18854             //Roo.log("adding to ");
18855             this.el = Roo.get(document.body).createChild(cfg, position);
18856 //            Roo.log(this.el);
18857         }
18858         this.initEvents();
18859     },
18860     
18861     initEvents : function()
18862     {
18863         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18864         this.el.enableDisplayMode('block');
18865         this.el.hide();
18866         if (this.over === false) {
18867             return; 
18868         }
18869         if (this.triggers === false) {
18870             return;
18871         }
18872         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18873         var triggers = this.trigger ? this.trigger.split(' ') : [];
18874         Roo.each(triggers, function(trigger) {
18875         
18876             if (trigger == 'click') {
18877                 on_el.on('click', this.toggle, this);
18878             } else if (trigger != 'manual') {
18879                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18880                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18881       
18882                 on_el.on(eventIn  ,this.enter, this);
18883                 on_el.on(eventOut, this.leave, this);
18884             }
18885         }, this);
18886         
18887     },
18888     
18889     
18890     // private
18891     timeout : null,
18892     hoverState : null,
18893     
18894     toggle : function () {
18895         this.hoverState == 'in' ? this.leave() : this.enter();
18896     },
18897     
18898     enter : function () {
18899         
18900         clearTimeout(this.timeout);
18901     
18902         this.hoverState = 'in';
18903     
18904         if (!this.delay || !this.delay.show) {
18905             this.show();
18906             return;
18907         }
18908         var _t = this;
18909         this.timeout = setTimeout(function () {
18910             if (_t.hoverState == 'in') {
18911                 _t.show();
18912             }
18913         }, this.delay.show)
18914     },
18915     
18916     leave : function() {
18917         clearTimeout(this.timeout);
18918     
18919         this.hoverState = 'out';
18920     
18921         if (!this.delay || !this.delay.hide) {
18922             this.hide();
18923             return;
18924         }
18925         var _t = this;
18926         this.timeout = setTimeout(function () {
18927             if (_t.hoverState == 'out') {
18928                 _t.hide();
18929             }
18930         }, this.delay.hide)
18931     },
18932     
18933     show : function (on_el)
18934     {
18935         if (!on_el) {
18936             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18937         }
18938         
18939         // set content.
18940         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18941         if (this.html !== false) {
18942             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18943         }
18944         this.el.removeClass([
18945             'fade','top','bottom', 'left', 'right','in',
18946             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18947         ]);
18948         if (!this.title.length) {
18949             this.el.select('.popover-title',true).hide();
18950         }
18951         
18952         var placement = typeof this.placement == 'function' ?
18953             this.placement.call(this, this.el, on_el) :
18954             this.placement;
18955             
18956         var autoToken = /\s?auto?\s?/i;
18957         var autoPlace = autoToken.test(placement);
18958         if (autoPlace) {
18959             placement = placement.replace(autoToken, '') || 'top';
18960         }
18961         
18962         //this.el.detach()
18963         //this.el.setXY([0,0]);
18964         this.el.show();
18965         this.el.dom.style.display='block';
18966         this.el.addClass(placement);
18967         
18968         //this.el.appendTo(on_el);
18969         
18970         var p = this.getPosition();
18971         var box = this.el.getBox();
18972         
18973         if (autoPlace) {
18974             // fixme..
18975         }
18976         var align = Roo.bootstrap.Popover.alignment[placement];
18977         
18978 //        Roo.log(align);
18979         this.el.alignTo(on_el, align[0],align[1]);
18980         //var arrow = this.el.select('.arrow',true).first();
18981         //arrow.set(align[2], 
18982         
18983         this.el.addClass('in');
18984         
18985         
18986         if (this.el.hasClass('fade')) {
18987             // fade it?
18988         }
18989         
18990         this.hoverState = 'in';
18991         
18992         this.fireEvent('show', this);
18993         
18994     },
18995     hide : function()
18996     {
18997         this.el.setXY([0,0]);
18998         this.el.removeClass('in');
18999         this.el.hide();
19000         this.hoverState = null;
19001         
19002         this.fireEvent('hide', this);
19003     }
19004     
19005 });
19006
19007 Roo.bootstrap.Popover.alignment = {
19008     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19009     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19010     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19011     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19012 };
19013
19014  /*
19015  * - LGPL
19016  *
19017  * Progress
19018  * 
19019  */
19020
19021 /**
19022  * @class Roo.bootstrap.Progress
19023  * @extends Roo.bootstrap.Component
19024  * Bootstrap Progress class
19025  * @cfg {Boolean} striped striped of the progress bar
19026  * @cfg {Boolean} active animated of the progress bar
19027  * 
19028  * 
19029  * @constructor
19030  * Create a new Progress
19031  * @param {Object} config The config object
19032  */
19033
19034 Roo.bootstrap.Progress = function(config){
19035     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19036 };
19037
19038 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19039     
19040     striped : false,
19041     active: false,
19042     
19043     getAutoCreate : function(){
19044         var cfg = {
19045             tag: 'div',
19046             cls: 'progress'
19047         };
19048         
19049         
19050         if(this.striped){
19051             cfg.cls += ' progress-striped';
19052         }
19053       
19054         if(this.active){
19055             cfg.cls += ' active';
19056         }
19057         
19058         
19059         return cfg;
19060     }
19061    
19062 });
19063
19064  
19065
19066  /*
19067  * - LGPL
19068  *
19069  * ProgressBar
19070  * 
19071  */
19072
19073 /**
19074  * @class Roo.bootstrap.ProgressBar
19075  * @extends Roo.bootstrap.Component
19076  * Bootstrap ProgressBar class
19077  * @cfg {Number} aria_valuenow aria-value now
19078  * @cfg {Number} aria_valuemin aria-value min
19079  * @cfg {Number} aria_valuemax aria-value max
19080  * @cfg {String} label label for the progress bar
19081  * @cfg {String} panel (success | info | warning | danger )
19082  * @cfg {String} role role of the progress bar
19083  * @cfg {String} sr_only text
19084  * 
19085  * 
19086  * @constructor
19087  * Create a new ProgressBar
19088  * @param {Object} config The config object
19089  */
19090
19091 Roo.bootstrap.ProgressBar = function(config){
19092     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19093 };
19094
19095 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19096     
19097     aria_valuenow : 0,
19098     aria_valuemin : 0,
19099     aria_valuemax : 100,
19100     label : false,
19101     panel : false,
19102     role : false,
19103     sr_only: false,
19104     
19105     getAutoCreate : function()
19106     {
19107         
19108         var cfg = {
19109             tag: 'div',
19110             cls: 'progress-bar',
19111             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19112         };
19113         
19114         if(this.sr_only){
19115             cfg.cn = {
19116                 tag: 'span',
19117                 cls: 'sr-only',
19118                 html: this.sr_only
19119             }
19120         }
19121         
19122         if(this.role){
19123             cfg.role = this.role;
19124         }
19125         
19126         if(this.aria_valuenow){
19127             cfg['aria-valuenow'] = this.aria_valuenow;
19128         }
19129         
19130         if(this.aria_valuemin){
19131             cfg['aria-valuemin'] = this.aria_valuemin;
19132         }
19133         
19134         if(this.aria_valuemax){
19135             cfg['aria-valuemax'] = this.aria_valuemax;
19136         }
19137         
19138         if(this.label && !this.sr_only){
19139             cfg.html = this.label;
19140         }
19141         
19142         if(this.panel){
19143             cfg.cls += ' progress-bar-' + this.panel;
19144         }
19145         
19146         return cfg;
19147     },
19148     
19149     update : function(aria_valuenow)
19150     {
19151         this.aria_valuenow = aria_valuenow;
19152         
19153         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19154     }
19155    
19156 });
19157
19158  
19159
19160  /*
19161  * - LGPL
19162  *
19163  * column
19164  * 
19165  */
19166
19167 /**
19168  * @class Roo.bootstrap.TabGroup
19169  * @extends Roo.bootstrap.Column
19170  * Bootstrap Column class
19171  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19172  * @cfg {Boolean} carousel true to make the group behave like a carousel
19173  * @cfg {Boolean} bullets show bullets for the panels
19174  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19175  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19176  * @cfg {Boolean} showarrow (true|false) show arrow default true
19177  * 
19178  * @constructor
19179  * Create a new TabGroup
19180  * @param {Object} config The config object
19181  */
19182
19183 Roo.bootstrap.TabGroup = function(config){
19184     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19185     if (!this.navId) {
19186         this.navId = Roo.id();
19187     }
19188     this.tabs = [];
19189     Roo.bootstrap.TabGroup.register(this);
19190     
19191 };
19192
19193 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19194     
19195     carousel : false,
19196     transition : false,
19197     bullets : 0,
19198     timer : 0,
19199     autoslide : false,
19200     slideFn : false,
19201     slideOnTouch : false,
19202     showarrow : true,
19203     
19204     getAutoCreate : function()
19205     {
19206         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19207         
19208         cfg.cls += ' tab-content';
19209         
19210         if (this.carousel) {
19211             cfg.cls += ' carousel slide';
19212             
19213             cfg.cn = [{
19214                cls : 'carousel-inner',
19215                cn : []
19216             }];
19217         
19218             if(this.bullets  && !Roo.isTouch){
19219                 
19220                 var bullets = {
19221                     cls : 'carousel-bullets',
19222                     cn : []
19223                 };
19224                
19225                 if(this.bullets_cls){
19226                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19227                 }
19228                 
19229                 bullets.cn.push({
19230                     cls : 'clear'
19231                 });
19232                 
19233                 cfg.cn[0].cn.push(bullets);
19234             }
19235             
19236             if(this.showarrow){
19237                 cfg.cn[0].cn.push({
19238                     tag : 'div',
19239                     class : 'carousel-arrow',
19240                     cn : [
19241                         {
19242                             tag : 'div',
19243                             class : 'carousel-prev',
19244                             cn : [
19245                                 {
19246                                     tag : 'i',
19247                                     class : 'fa fa-chevron-left'
19248                                 }
19249                             ]
19250                         },
19251                         {
19252                             tag : 'div',
19253                             class : 'carousel-next',
19254                             cn : [
19255                                 {
19256                                     tag : 'i',
19257                                     class : 'fa fa-chevron-right'
19258                                 }
19259                             ]
19260                         }
19261                     ]
19262                 });
19263             }
19264             
19265         }
19266         
19267         return cfg;
19268     },
19269     
19270     initEvents:  function()
19271     {
19272 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19273 //            this.el.on("touchstart", this.onTouchStart, this);
19274 //        }
19275         
19276         if(this.autoslide){
19277             var _this = this;
19278             
19279             this.slideFn = window.setInterval(function() {
19280                 _this.showPanelNext();
19281             }, this.timer);
19282         }
19283         
19284         if(this.showarrow){
19285             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19286             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19287         }
19288         
19289         
19290     },
19291     
19292 //    onTouchStart : function(e, el, o)
19293 //    {
19294 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19295 //            return;
19296 //        }
19297 //        
19298 //        this.showPanelNext();
19299 //    },
19300     
19301     
19302     getChildContainer : function()
19303     {
19304         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19305     },
19306     
19307     /**
19308     * register a Navigation item
19309     * @param {Roo.bootstrap.NavItem} the navitem to add
19310     */
19311     register : function(item)
19312     {
19313         this.tabs.push( item);
19314         item.navId = this.navId; // not really needed..
19315         this.addBullet();
19316     
19317     },
19318     
19319     getActivePanel : function()
19320     {
19321         var r = false;
19322         Roo.each(this.tabs, function(t) {
19323             if (t.active) {
19324                 r = t;
19325                 return false;
19326             }
19327             return null;
19328         });
19329         return r;
19330         
19331     },
19332     getPanelByName : function(n)
19333     {
19334         var r = false;
19335         Roo.each(this.tabs, function(t) {
19336             if (t.tabId == n) {
19337                 r = t;
19338                 return false;
19339             }
19340             return null;
19341         });
19342         return r;
19343     },
19344     indexOfPanel : function(p)
19345     {
19346         var r = false;
19347         Roo.each(this.tabs, function(t,i) {
19348             if (t.tabId == p.tabId) {
19349                 r = i;
19350                 return false;
19351             }
19352             return null;
19353         });
19354         return r;
19355     },
19356     /**
19357      * show a specific panel
19358      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19359      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19360      */
19361     showPanel : function (pan)
19362     {
19363         if(this.transition || typeof(pan) == 'undefined'){
19364             Roo.log("waiting for the transitionend");
19365             return false;
19366         }
19367         
19368         if (typeof(pan) == 'number') {
19369             pan = this.tabs[pan];
19370         }
19371         
19372         if (typeof(pan) == 'string') {
19373             pan = this.getPanelByName(pan);
19374         }
19375         
19376         var cur = this.getActivePanel();
19377         
19378         if(!pan || !cur){
19379             Roo.log('pan or acitve pan is undefined');
19380             return false;
19381         }
19382         
19383         if (pan.tabId == this.getActivePanel().tabId) {
19384             return true;
19385         }
19386         
19387         if (false === cur.fireEvent('beforedeactivate')) {
19388             return false;
19389         }
19390         
19391         if(this.bullets > 0 && !Roo.isTouch){
19392             this.setActiveBullet(this.indexOfPanel(pan));
19393         }
19394         
19395         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19396             
19397             //class="carousel-item carousel-item-next carousel-item-left"
19398             
19399             this.transition = true;
19400             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19401             var lr = dir == 'next' ? 'left' : 'right';
19402             pan.el.addClass(dir); // or prev
19403             pan.el.addClass('carousel-item-' + dir); // or prev
19404             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19405             cur.el.addClass(lr); // or right
19406             pan.el.addClass(lr);
19407             cur.el.addClass('carousel-item-' +lr); // or right
19408             pan.el.addClass('carousel-item-' +lr);
19409             
19410             
19411             var _this = this;
19412             cur.el.on('transitionend', function() {
19413                 Roo.log("trans end?");
19414                 
19415                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19416                 pan.setActive(true);
19417                 
19418                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19419                 cur.setActive(false);
19420                 
19421                 _this.transition = false;
19422                 
19423             }, this, { single:  true } );
19424             
19425             return true;
19426         }
19427         
19428         cur.setActive(false);
19429         pan.setActive(true);
19430         
19431         return true;
19432         
19433     },
19434     showPanelNext : function()
19435     {
19436         var i = this.indexOfPanel(this.getActivePanel());
19437         
19438         if (i >= this.tabs.length - 1 && !this.autoslide) {
19439             return;
19440         }
19441         
19442         if (i >= this.tabs.length - 1 && this.autoslide) {
19443             i = -1;
19444         }
19445         
19446         this.showPanel(this.tabs[i+1]);
19447     },
19448     
19449     showPanelPrev : function()
19450     {
19451         var i = this.indexOfPanel(this.getActivePanel());
19452         
19453         if (i  < 1 && !this.autoslide) {
19454             return;
19455         }
19456         
19457         if (i < 1 && this.autoslide) {
19458             i = this.tabs.length;
19459         }
19460         
19461         this.showPanel(this.tabs[i-1]);
19462     },
19463     
19464     
19465     addBullet: function()
19466     {
19467         if(!this.bullets || Roo.isTouch){
19468             return;
19469         }
19470         var ctr = this.el.select('.carousel-bullets',true).first();
19471         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19472         var bullet = ctr.createChild({
19473             cls : 'bullet bullet-' + i
19474         },ctr.dom.lastChild);
19475         
19476         
19477         var _this = this;
19478         
19479         bullet.on('click', (function(e, el, o, ii, t){
19480
19481             e.preventDefault();
19482
19483             this.showPanel(ii);
19484
19485             if(this.autoslide && this.slideFn){
19486                 clearInterval(this.slideFn);
19487                 this.slideFn = window.setInterval(function() {
19488                     _this.showPanelNext();
19489                 }, this.timer);
19490             }
19491
19492         }).createDelegate(this, [i, bullet], true));
19493                 
19494         
19495     },
19496      
19497     setActiveBullet : function(i)
19498     {
19499         if(Roo.isTouch){
19500             return;
19501         }
19502         
19503         Roo.each(this.el.select('.bullet', true).elements, function(el){
19504             el.removeClass('selected');
19505         });
19506
19507         var bullet = this.el.select('.bullet-' + i, true).first();
19508         
19509         if(!bullet){
19510             return;
19511         }
19512         
19513         bullet.addClass('selected');
19514     }
19515     
19516     
19517   
19518 });
19519
19520  
19521
19522  
19523  
19524 Roo.apply(Roo.bootstrap.TabGroup, {
19525     
19526     groups: {},
19527      /**
19528     * register a Navigation Group
19529     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19530     */
19531     register : function(navgrp)
19532     {
19533         this.groups[navgrp.navId] = navgrp;
19534         
19535     },
19536     /**
19537     * fetch a Navigation Group based on the navigation ID
19538     * if one does not exist , it will get created.
19539     * @param {string} the navgroup to add
19540     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19541     */
19542     get: function(navId) {
19543         if (typeof(this.groups[navId]) == 'undefined') {
19544             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19545         }
19546         return this.groups[navId] ;
19547     }
19548     
19549     
19550     
19551 });
19552
19553  /*
19554  * - LGPL
19555  *
19556  * TabPanel
19557  * 
19558  */
19559
19560 /**
19561  * @class Roo.bootstrap.TabPanel
19562  * @extends Roo.bootstrap.Component
19563  * Bootstrap TabPanel class
19564  * @cfg {Boolean} active panel active
19565  * @cfg {String} html panel content
19566  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19567  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19568  * @cfg {String} href click to link..
19569  * 
19570  * 
19571  * @constructor
19572  * Create a new TabPanel
19573  * @param {Object} config The config object
19574  */
19575
19576 Roo.bootstrap.TabPanel = function(config){
19577     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19578     this.addEvents({
19579         /**
19580              * @event changed
19581              * Fires when the active status changes
19582              * @param {Roo.bootstrap.TabPanel} this
19583              * @param {Boolean} state the new state
19584             
19585          */
19586         'changed': true,
19587         /**
19588              * @event beforedeactivate
19589              * Fires before a tab is de-activated - can be used to do validation on a form.
19590              * @param {Roo.bootstrap.TabPanel} this
19591              * @return {Boolean} false if there is an error
19592             
19593          */
19594         'beforedeactivate': true
19595      });
19596     
19597     this.tabId = this.tabId || Roo.id();
19598   
19599 };
19600
19601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19602     
19603     active: false,
19604     html: false,
19605     tabId: false,
19606     navId : false,
19607     href : '',
19608     
19609     getAutoCreate : function(){
19610         
19611         
19612         var cfg = {
19613             tag: 'div',
19614             // item is needed for carousel - not sure if it has any effect otherwise
19615             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19616             html: this.html || ''
19617         };
19618         
19619         if(this.active){
19620             cfg.cls += ' active';
19621         }
19622         
19623         if(this.tabId){
19624             cfg.tabId = this.tabId;
19625         }
19626         
19627         
19628         
19629         return cfg;
19630     },
19631     
19632     initEvents:  function()
19633     {
19634         var p = this.parent();
19635         
19636         this.navId = this.navId || p.navId;
19637         
19638         if (typeof(this.navId) != 'undefined') {
19639             // not really needed.. but just in case.. parent should be a NavGroup.
19640             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19641             
19642             tg.register(this);
19643             
19644             var i = tg.tabs.length - 1;
19645             
19646             if(this.active && tg.bullets > 0 && i < tg.bullets){
19647                 tg.setActiveBullet(i);
19648             }
19649         }
19650         
19651         this.el.on('click', this.onClick, this);
19652         
19653         if(Roo.isTouch){
19654             this.el.on("touchstart", this.onTouchStart, this);
19655             this.el.on("touchmove", this.onTouchMove, this);
19656             this.el.on("touchend", this.onTouchEnd, this);
19657         }
19658         
19659     },
19660     
19661     onRender : function(ct, position)
19662     {
19663         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19664     },
19665     
19666     setActive : function(state)
19667     {
19668         Roo.log("panel - set active " + this.tabId + "=" + state);
19669         
19670         this.active = state;
19671         if (!state) {
19672             this.el.removeClass('active');
19673             
19674         } else  if (!this.el.hasClass('active')) {
19675             this.el.addClass('active');
19676         }
19677         
19678         this.fireEvent('changed', this, state);
19679     },
19680     
19681     onClick : function(e)
19682     {
19683         e.preventDefault();
19684         
19685         if(!this.href.length){
19686             return;
19687         }
19688         
19689         window.location.href = this.href;
19690     },
19691     
19692     startX : 0,
19693     startY : 0,
19694     endX : 0,
19695     endY : 0,
19696     swiping : false,
19697     
19698     onTouchStart : function(e)
19699     {
19700         this.swiping = false;
19701         
19702         this.startX = e.browserEvent.touches[0].clientX;
19703         this.startY = e.browserEvent.touches[0].clientY;
19704     },
19705     
19706     onTouchMove : function(e)
19707     {
19708         this.swiping = true;
19709         
19710         this.endX = e.browserEvent.touches[0].clientX;
19711         this.endY = e.browserEvent.touches[0].clientY;
19712     },
19713     
19714     onTouchEnd : function(e)
19715     {
19716         if(!this.swiping){
19717             this.onClick(e);
19718             return;
19719         }
19720         
19721         var tabGroup = this.parent();
19722         
19723         if(this.endX > this.startX){ // swiping right
19724             tabGroup.showPanelPrev();
19725             return;
19726         }
19727         
19728         if(this.startX > this.endX){ // swiping left
19729             tabGroup.showPanelNext();
19730             return;
19731         }
19732     }
19733     
19734     
19735 });
19736  
19737
19738  
19739
19740  /*
19741  * - LGPL
19742  *
19743  * DateField
19744  * 
19745  */
19746
19747 /**
19748  * @class Roo.bootstrap.DateField
19749  * @extends Roo.bootstrap.Input
19750  * Bootstrap DateField class
19751  * @cfg {Number} weekStart default 0
19752  * @cfg {String} viewMode default empty, (months|years)
19753  * @cfg {String} minViewMode default empty, (months|years)
19754  * @cfg {Number} startDate default -Infinity
19755  * @cfg {Number} endDate default Infinity
19756  * @cfg {Boolean} todayHighlight default false
19757  * @cfg {Boolean} todayBtn default false
19758  * @cfg {Boolean} calendarWeeks default false
19759  * @cfg {Object} daysOfWeekDisabled default empty
19760  * @cfg {Boolean} singleMode default false (true | false)
19761  * 
19762  * @cfg {Boolean} keyboardNavigation default true
19763  * @cfg {String} language default en
19764  * 
19765  * @constructor
19766  * Create a new DateField
19767  * @param {Object} config The config object
19768  */
19769
19770 Roo.bootstrap.DateField = function(config){
19771     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19772      this.addEvents({
19773             /**
19774              * @event show
19775              * Fires when this field show.
19776              * @param {Roo.bootstrap.DateField} this
19777              * @param {Mixed} date The date value
19778              */
19779             show : true,
19780             /**
19781              * @event show
19782              * Fires when this field hide.
19783              * @param {Roo.bootstrap.DateField} this
19784              * @param {Mixed} date The date value
19785              */
19786             hide : true,
19787             /**
19788              * @event select
19789              * Fires when select a date.
19790              * @param {Roo.bootstrap.DateField} this
19791              * @param {Mixed} date The date value
19792              */
19793             select : true,
19794             /**
19795              * @event beforeselect
19796              * Fires when before select a date.
19797              * @param {Roo.bootstrap.DateField} this
19798              * @param {Mixed} date The date value
19799              */
19800             beforeselect : true
19801         });
19802 };
19803
19804 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19805     
19806     /**
19807      * @cfg {String} format
19808      * The default date format string which can be overriden for localization support.  The format must be
19809      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19810      */
19811     format : "m/d/y",
19812     /**
19813      * @cfg {String} altFormats
19814      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19815      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19816      */
19817     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19818     
19819     weekStart : 0,
19820     
19821     viewMode : '',
19822     
19823     minViewMode : '',
19824     
19825     todayHighlight : false,
19826     
19827     todayBtn: false,
19828     
19829     language: 'en',
19830     
19831     keyboardNavigation: true,
19832     
19833     calendarWeeks: false,
19834     
19835     startDate: -Infinity,
19836     
19837     endDate: Infinity,
19838     
19839     daysOfWeekDisabled: [],
19840     
19841     _events: [],
19842     
19843     singleMode : false,
19844     
19845     UTCDate: function()
19846     {
19847         return new Date(Date.UTC.apply(Date, arguments));
19848     },
19849     
19850     UTCToday: function()
19851     {
19852         var today = new Date();
19853         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19854     },
19855     
19856     getDate: function() {
19857             var d = this.getUTCDate();
19858             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19859     },
19860     
19861     getUTCDate: function() {
19862             return this.date;
19863     },
19864     
19865     setDate: function(d) {
19866             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19867     },
19868     
19869     setUTCDate: function(d) {
19870             this.date = d;
19871             this.setValue(this.formatDate(this.date));
19872     },
19873         
19874     onRender: function(ct, position)
19875     {
19876         
19877         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19878         
19879         this.language = this.language || 'en';
19880         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19881         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19882         
19883         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19884         this.format = this.format || 'm/d/y';
19885         this.isInline = false;
19886         this.isInput = true;
19887         this.component = this.el.select('.add-on', true).first() || false;
19888         this.component = (this.component && this.component.length === 0) ? false : this.component;
19889         this.hasInput = this.component && this.inputEl().length;
19890         
19891         if (typeof(this.minViewMode === 'string')) {
19892             switch (this.minViewMode) {
19893                 case 'months':
19894                     this.minViewMode = 1;
19895                     break;
19896                 case 'years':
19897                     this.minViewMode = 2;
19898                     break;
19899                 default:
19900                     this.minViewMode = 0;
19901                     break;
19902             }
19903         }
19904         
19905         if (typeof(this.viewMode === 'string')) {
19906             switch (this.viewMode) {
19907                 case 'months':
19908                     this.viewMode = 1;
19909                     break;
19910                 case 'years':
19911                     this.viewMode = 2;
19912                     break;
19913                 default:
19914                     this.viewMode = 0;
19915                     break;
19916             }
19917         }
19918                 
19919         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19920         
19921 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19922         
19923         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19924         
19925         this.picker().on('mousedown', this.onMousedown, this);
19926         this.picker().on('click', this.onClick, this);
19927         
19928         this.picker().addClass('datepicker-dropdown');
19929         
19930         this.startViewMode = this.viewMode;
19931         
19932         if(this.singleMode){
19933             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19934                 v.setVisibilityMode(Roo.Element.DISPLAY);
19935                 v.hide();
19936             });
19937             
19938             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19939                 v.setStyle('width', '189px');
19940             });
19941         }
19942         
19943         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19944             if(!this.calendarWeeks){
19945                 v.remove();
19946                 return;
19947             }
19948             
19949             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19950             v.attr('colspan', function(i, val){
19951                 return parseInt(val) + 1;
19952             });
19953         });
19954                         
19955         
19956         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19957         
19958         this.setStartDate(this.startDate);
19959         this.setEndDate(this.endDate);
19960         
19961         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19962         
19963         this.fillDow();
19964         this.fillMonths();
19965         this.update();
19966         this.showMode();
19967         
19968         if(this.isInline) {
19969             this.showPopup();
19970         }
19971     },
19972     
19973     picker : function()
19974     {
19975         return this.pickerEl;
19976 //        return this.el.select('.datepicker', true).first();
19977     },
19978     
19979     fillDow: function()
19980     {
19981         var dowCnt = this.weekStart;
19982         
19983         var dow = {
19984             tag: 'tr',
19985             cn: [
19986                 
19987             ]
19988         };
19989         
19990         if(this.calendarWeeks){
19991             dow.cn.push({
19992                 tag: 'th',
19993                 cls: 'cw',
19994                 html: '&nbsp;'
19995             })
19996         }
19997         
19998         while (dowCnt < this.weekStart + 7) {
19999             dow.cn.push({
20000                 tag: 'th',
20001                 cls: 'dow',
20002                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20003             });
20004         }
20005         
20006         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20007     },
20008     
20009     fillMonths: function()
20010     {    
20011         var i = 0;
20012         var months = this.picker().select('>.datepicker-months td', true).first();
20013         
20014         months.dom.innerHTML = '';
20015         
20016         while (i < 12) {
20017             var month = {
20018                 tag: 'span',
20019                 cls: 'month',
20020                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20021             };
20022             
20023             months.createChild(month);
20024         }
20025         
20026     },
20027     
20028     update: function()
20029     {
20030         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;
20031         
20032         if (this.date < this.startDate) {
20033             this.viewDate = new Date(this.startDate);
20034         } else if (this.date > this.endDate) {
20035             this.viewDate = new Date(this.endDate);
20036         } else {
20037             this.viewDate = new Date(this.date);
20038         }
20039         
20040         this.fill();
20041     },
20042     
20043     fill: function() 
20044     {
20045         var d = new Date(this.viewDate),
20046                 year = d.getUTCFullYear(),
20047                 month = d.getUTCMonth(),
20048                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20049                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20050                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20051                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20052                 currentDate = this.date && this.date.valueOf(),
20053                 today = this.UTCToday();
20054         
20055         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20056         
20057 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20058         
20059 //        this.picker.select('>tfoot th.today').
20060 //                                              .text(dates[this.language].today)
20061 //                                              .toggle(this.todayBtn !== false);
20062     
20063         this.updateNavArrows();
20064         this.fillMonths();
20065                                                 
20066         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20067         
20068         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20069          
20070         prevMonth.setUTCDate(day);
20071         
20072         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20073         
20074         var nextMonth = new Date(prevMonth);
20075         
20076         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20077         
20078         nextMonth = nextMonth.valueOf();
20079         
20080         var fillMonths = false;
20081         
20082         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20083         
20084         while(prevMonth.valueOf() <= nextMonth) {
20085             var clsName = '';
20086             
20087             if (prevMonth.getUTCDay() === this.weekStart) {
20088                 if(fillMonths){
20089                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20090                 }
20091                     
20092                 fillMonths = {
20093                     tag: 'tr',
20094                     cn: []
20095                 };
20096                 
20097                 if(this.calendarWeeks){
20098                     // ISO 8601: First week contains first thursday.
20099                     // ISO also states week starts on Monday, but we can be more abstract here.
20100                     var
20101                     // Start of current week: based on weekstart/current date
20102                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20103                     // Thursday of this week
20104                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20105                     // First Thursday of year, year from thursday
20106                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20107                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20108                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20109                     
20110                     fillMonths.cn.push({
20111                         tag: 'td',
20112                         cls: 'cw',
20113                         html: calWeek
20114                     });
20115                 }
20116             }
20117             
20118             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20119                 clsName += ' old';
20120             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20121                 clsName += ' new';
20122             }
20123             if (this.todayHighlight &&
20124                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20125                 prevMonth.getUTCMonth() == today.getMonth() &&
20126                 prevMonth.getUTCDate() == today.getDate()) {
20127                 clsName += ' today';
20128             }
20129             
20130             if (currentDate && prevMonth.valueOf() === currentDate) {
20131                 clsName += ' active';
20132             }
20133             
20134             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20135                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20136                     clsName += ' disabled';
20137             }
20138             
20139             fillMonths.cn.push({
20140                 tag: 'td',
20141                 cls: 'day ' + clsName,
20142                 html: prevMonth.getDate()
20143             });
20144             
20145             prevMonth.setDate(prevMonth.getDate()+1);
20146         }
20147           
20148         var currentYear = this.date && this.date.getUTCFullYear();
20149         var currentMonth = this.date && this.date.getUTCMonth();
20150         
20151         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20152         
20153         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20154             v.removeClass('active');
20155             
20156             if(currentYear === year && k === currentMonth){
20157                 v.addClass('active');
20158             }
20159             
20160             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20161                 v.addClass('disabled');
20162             }
20163             
20164         });
20165         
20166         
20167         year = parseInt(year/10, 10) * 10;
20168         
20169         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20170         
20171         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20172         
20173         year -= 1;
20174         for (var i = -1; i < 11; i++) {
20175             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20176                 tag: 'span',
20177                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20178                 html: year
20179             });
20180             
20181             year += 1;
20182         }
20183     },
20184     
20185     showMode: function(dir) 
20186     {
20187         if (dir) {
20188             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20189         }
20190         
20191         Roo.each(this.picker().select('>div',true).elements, function(v){
20192             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20193             v.hide();
20194         });
20195         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20196     },
20197     
20198     place: function()
20199     {
20200         if(this.isInline) {
20201             return;
20202         }
20203         
20204         this.picker().removeClass(['bottom', 'top']);
20205         
20206         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20207             /*
20208              * place to the top of element!
20209              *
20210              */
20211             
20212             this.picker().addClass('top');
20213             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20214             
20215             return;
20216         }
20217         
20218         this.picker().addClass('bottom');
20219         
20220         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20221     },
20222     
20223     parseDate : function(value)
20224     {
20225         if(!value || value instanceof Date){
20226             return value;
20227         }
20228         var v = Date.parseDate(value, this.format);
20229         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20230             v = Date.parseDate(value, 'Y-m-d');
20231         }
20232         if(!v && this.altFormats){
20233             if(!this.altFormatsArray){
20234                 this.altFormatsArray = this.altFormats.split("|");
20235             }
20236             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20237                 v = Date.parseDate(value, this.altFormatsArray[i]);
20238             }
20239         }
20240         return v;
20241     },
20242     
20243     formatDate : function(date, fmt)
20244     {   
20245         return (!date || !(date instanceof Date)) ?
20246         date : date.dateFormat(fmt || this.format);
20247     },
20248     
20249     onFocus : function()
20250     {
20251         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20252         this.showPopup();
20253     },
20254     
20255     onBlur : function()
20256     {
20257         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20258         
20259         var d = this.inputEl().getValue();
20260         
20261         this.setValue(d);
20262                 
20263         this.hidePopup();
20264     },
20265     
20266     showPopup : function()
20267     {
20268         this.picker().show();
20269         this.update();
20270         this.place();
20271         
20272         this.fireEvent('showpopup', this, this.date);
20273     },
20274     
20275     hidePopup : function()
20276     {
20277         if(this.isInline) {
20278             return;
20279         }
20280         this.picker().hide();
20281         this.viewMode = this.startViewMode;
20282         this.showMode();
20283         
20284         this.fireEvent('hidepopup', this, this.date);
20285         
20286     },
20287     
20288     onMousedown: function(e)
20289     {
20290         e.stopPropagation();
20291         e.preventDefault();
20292     },
20293     
20294     keyup: function(e)
20295     {
20296         Roo.bootstrap.DateField.superclass.keyup.call(this);
20297         this.update();
20298     },
20299
20300     setValue: function(v)
20301     {
20302         if(this.fireEvent('beforeselect', this, v) !== false){
20303             var d = new Date(this.parseDate(v) ).clearTime();
20304         
20305             if(isNaN(d.getTime())){
20306                 this.date = this.viewDate = '';
20307                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20308                 return;
20309             }
20310
20311             v = this.formatDate(d);
20312
20313             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20314
20315             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20316
20317             this.update();
20318
20319             this.fireEvent('select', this, this.date);
20320         }
20321     },
20322     
20323     getValue: function()
20324     {
20325         return this.formatDate(this.date);
20326     },
20327     
20328     fireKey: function(e)
20329     {
20330         if (!this.picker().isVisible()){
20331             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20332                 this.showPopup();
20333             }
20334             return;
20335         }
20336         
20337         var dateChanged = false,
20338         dir, day, month,
20339         newDate, newViewDate;
20340         
20341         switch(e.keyCode){
20342             case 27: // escape
20343                 this.hidePopup();
20344                 e.preventDefault();
20345                 break;
20346             case 37: // left
20347             case 39: // right
20348                 if (!this.keyboardNavigation) {
20349                     break;
20350                 }
20351                 dir = e.keyCode == 37 ? -1 : 1;
20352                 
20353                 if (e.ctrlKey){
20354                     newDate = this.moveYear(this.date, dir);
20355                     newViewDate = this.moveYear(this.viewDate, dir);
20356                 } else if (e.shiftKey){
20357                     newDate = this.moveMonth(this.date, dir);
20358                     newViewDate = this.moveMonth(this.viewDate, dir);
20359                 } else {
20360                     newDate = new Date(this.date);
20361                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20362                     newViewDate = new Date(this.viewDate);
20363                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20364                 }
20365                 if (this.dateWithinRange(newDate)){
20366                     this.date = newDate;
20367                     this.viewDate = newViewDate;
20368                     this.setValue(this.formatDate(this.date));
20369 //                    this.update();
20370                     e.preventDefault();
20371                     dateChanged = true;
20372                 }
20373                 break;
20374             case 38: // up
20375             case 40: // down
20376                 if (!this.keyboardNavigation) {
20377                     break;
20378                 }
20379                 dir = e.keyCode == 38 ? -1 : 1;
20380                 if (e.ctrlKey){
20381                     newDate = this.moveYear(this.date, dir);
20382                     newViewDate = this.moveYear(this.viewDate, dir);
20383                 } else if (e.shiftKey){
20384                     newDate = this.moveMonth(this.date, dir);
20385                     newViewDate = this.moveMonth(this.viewDate, dir);
20386                 } else {
20387                     newDate = new Date(this.date);
20388                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20389                     newViewDate = new Date(this.viewDate);
20390                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20391                 }
20392                 if (this.dateWithinRange(newDate)){
20393                     this.date = newDate;
20394                     this.viewDate = newViewDate;
20395                     this.setValue(this.formatDate(this.date));
20396 //                    this.update();
20397                     e.preventDefault();
20398                     dateChanged = true;
20399                 }
20400                 break;
20401             case 13: // enter
20402                 this.setValue(this.formatDate(this.date));
20403                 this.hidePopup();
20404                 e.preventDefault();
20405                 break;
20406             case 9: // tab
20407                 this.setValue(this.formatDate(this.date));
20408                 this.hidePopup();
20409                 break;
20410             case 16: // shift
20411             case 17: // ctrl
20412             case 18: // alt
20413                 break;
20414             default :
20415                 this.hidePopup();
20416                 
20417         }
20418     },
20419     
20420     
20421     onClick: function(e) 
20422     {
20423         e.stopPropagation();
20424         e.preventDefault();
20425         
20426         var target = e.getTarget();
20427         
20428         if(target.nodeName.toLowerCase() === 'i'){
20429             target = Roo.get(target).dom.parentNode;
20430         }
20431         
20432         var nodeName = target.nodeName;
20433         var className = target.className;
20434         var html = target.innerHTML;
20435         //Roo.log(nodeName);
20436         
20437         switch(nodeName.toLowerCase()) {
20438             case 'th':
20439                 switch(className) {
20440                     case 'switch':
20441                         this.showMode(1);
20442                         break;
20443                     case 'prev':
20444                     case 'next':
20445                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20446                         switch(this.viewMode){
20447                                 case 0:
20448                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20449                                         break;
20450                                 case 1:
20451                                 case 2:
20452                                         this.viewDate = this.moveYear(this.viewDate, dir);
20453                                         break;
20454                         }
20455                         this.fill();
20456                         break;
20457                     case 'today':
20458                         var date = new Date();
20459                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20460 //                        this.fill()
20461                         this.setValue(this.formatDate(this.date));
20462                         
20463                         this.hidePopup();
20464                         break;
20465                 }
20466                 break;
20467             case 'span':
20468                 if (className.indexOf('disabled') < 0) {
20469                     this.viewDate.setUTCDate(1);
20470                     if (className.indexOf('month') > -1) {
20471                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20472                     } else {
20473                         var year = parseInt(html, 10) || 0;
20474                         this.viewDate.setUTCFullYear(year);
20475                         
20476                     }
20477                     
20478                     if(this.singleMode){
20479                         this.setValue(this.formatDate(this.viewDate));
20480                         this.hidePopup();
20481                         return;
20482                     }
20483                     
20484                     this.showMode(-1);
20485                     this.fill();
20486                 }
20487                 break;
20488                 
20489             case 'td':
20490                 //Roo.log(className);
20491                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20492                     var day = parseInt(html, 10) || 1;
20493                     var year = this.viewDate.getUTCFullYear(),
20494                         month = this.viewDate.getUTCMonth();
20495
20496                     if (className.indexOf('old') > -1) {
20497                         if(month === 0 ){
20498                             month = 11;
20499                             year -= 1;
20500                         }else{
20501                             month -= 1;
20502                         }
20503                     } else if (className.indexOf('new') > -1) {
20504                         if (month == 11) {
20505                             month = 0;
20506                             year += 1;
20507                         } else {
20508                             month += 1;
20509                         }
20510                     }
20511                     //Roo.log([year,month,day]);
20512                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20513                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20514 //                    this.fill();
20515                     //Roo.log(this.formatDate(this.date));
20516                     this.setValue(this.formatDate(this.date));
20517                     this.hidePopup();
20518                 }
20519                 break;
20520         }
20521     },
20522     
20523     setStartDate: function(startDate)
20524     {
20525         this.startDate = startDate || -Infinity;
20526         if (this.startDate !== -Infinity) {
20527             this.startDate = this.parseDate(this.startDate);
20528         }
20529         this.update();
20530         this.updateNavArrows();
20531     },
20532
20533     setEndDate: function(endDate)
20534     {
20535         this.endDate = endDate || Infinity;
20536         if (this.endDate !== Infinity) {
20537             this.endDate = this.parseDate(this.endDate);
20538         }
20539         this.update();
20540         this.updateNavArrows();
20541     },
20542     
20543     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20544     {
20545         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20546         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20547             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20548         }
20549         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20550             return parseInt(d, 10);
20551         });
20552         this.update();
20553         this.updateNavArrows();
20554     },
20555     
20556     updateNavArrows: function() 
20557     {
20558         if(this.singleMode){
20559             return;
20560         }
20561         
20562         var d = new Date(this.viewDate),
20563         year = d.getUTCFullYear(),
20564         month = d.getUTCMonth();
20565         
20566         Roo.each(this.picker().select('.prev', true).elements, function(v){
20567             v.show();
20568             switch (this.viewMode) {
20569                 case 0:
20570
20571                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20572                         v.hide();
20573                     }
20574                     break;
20575                 case 1:
20576                 case 2:
20577                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20578                         v.hide();
20579                     }
20580                     break;
20581             }
20582         });
20583         
20584         Roo.each(this.picker().select('.next', true).elements, function(v){
20585             v.show();
20586             switch (this.viewMode) {
20587                 case 0:
20588
20589                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20590                         v.hide();
20591                     }
20592                     break;
20593                 case 1:
20594                 case 2:
20595                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20596                         v.hide();
20597                     }
20598                     break;
20599             }
20600         })
20601     },
20602     
20603     moveMonth: function(date, dir)
20604     {
20605         if (!dir) {
20606             return date;
20607         }
20608         var new_date = new Date(date.valueOf()),
20609         day = new_date.getUTCDate(),
20610         month = new_date.getUTCMonth(),
20611         mag = Math.abs(dir),
20612         new_month, test;
20613         dir = dir > 0 ? 1 : -1;
20614         if (mag == 1){
20615             test = dir == -1
20616             // If going back one month, make sure month is not current month
20617             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20618             ? function(){
20619                 return new_date.getUTCMonth() == month;
20620             }
20621             // If going forward one month, make sure month is as expected
20622             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20623             : function(){
20624                 return new_date.getUTCMonth() != new_month;
20625             };
20626             new_month = month + dir;
20627             new_date.setUTCMonth(new_month);
20628             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20629             if (new_month < 0 || new_month > 11) {
20630                 new_month = (new_month + 12) % 12;
20631             }
20632         } else {
20633             // For magnitudes >1, move one month at a time...
20634             for (var i=0; i<mag; i++) {
20635                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20636                 new_date = this.moveMonth(new_date, dir);
20637             }
20638             // ...then reset the day, keeping it in the new month
20639             new_month = new_date.getUTCMonth();
20640             new_date.setUTCDate(day);
20641             test = function(){
20642                 return new_month != new_date.getUTCMonth();
20643             };
20644         }
20645         // Common date-resetting loop -- if date is beyond end of month, make it
20646         // end of month
20647         while (test()){
20648             new_date.setUTCDate(--day);
20649             new_date.setUTCMonth(new_month);
20650         }
20651         return new_date;
20652     },
20653
20654     moveYear: function(date, dir)
20655     {
20656         return this.moveMonth(date, dir*12);
20657     },
20658
20659     dateWithinRange: function(date)
20660     {
20661         return date >= this.startDate && date <= this.endDate;
20662     },
20663
20664     
20665     remove: function() 
20666     {
20667         this.picker().remove();
20668     },
20669     
20670     validateValue : function(value)
20671     {
20672         if(this.getVisibilityEl().hasClass('hidden')){
20673             return true;
20674         }
20675         
20676         if(value.length < 1)  {
20677             if(this.allowBlank){
20678                 return true;
20679             }
20680             return false;
20681         }
20682         
20683         if(value.length < this.minLength){
20684             return false;
20685         }
20686         if(value.length > this.maxLength){
20687             return false;
20688         }
20689         if(this.vtype){
20690             var vt = Roo.form.VTypes;
20691             if(!vt[this.vtype](value, this)){
20692                 return false;
20693             }
20694         }
20695         if(typeof this.validator == "function"){
20696             var msg = this.validator(value);
20697             if(msg !== true){
20698                 return false;
20699             }
20700         }
20701         
20702         if(this.regex && !this.regex.test(value)){
20703             return false;
20704         }
20705         
20706         if(typeof(this.parseDate(value)) == 'undefined'){
20707             return false;
20708         }
20709         
20710         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20711             return false;
20712         }      
20713         
20714         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20715             return false;
20716         } 
20717         
20718         
20719         return true;
20720     },
20721     
20722     reset : function()
20723     {
20724         this.date = this.viewDate = '';
20725         
20726         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20727     }
20728    
20729 });
20730
20731 Roo.apply(Roo.bootstrap.DateField,  {
20732     
20733     head : {
20734         tag: 'thead',
20735         cn: [
20736         {
20737             tag: 'tr',
20738             cn: [
20739             {
20740                 tag: 'th',
20741                 cls: 'prev',
20742                 html: '<i class="fa fa-arrow-left"/>'
20743             },
20744             {
20745                 tag: 'th',
20746                 cls: 'switch',
20747                 colspan: '5'
20748             },
20749             {
20750                 tag: 'th',
20751                 cls: 'next',
20752                 html: '<i class="fa fa-arrow-right"/>'
20753             }
20754
20755             ]
20756         }
20757         ]
20758     },
20759     
20760     content : {
20761         tag: 'tbody',
20762         cn: [
20763         {
20764             tag: 'tr',
20765             cn: [
20766             {
20767                 tag: 'td',
20768                 colspan: '7'
20769             }
20770             ]
20771         }
20772         ]
20773     },
20774     
20775     footer : {
20776         tag: 'tfoot',
20777         cn: [
20778         {
20779             tag: 'tr',
20780             cn: [
20781             {
20782                 tag: 'th',
20783                 colspan: '7',
20784                 cls: 'today'
20785             }
20786                     
20787             ]
20788         }
20789         ]
20790     },
20791     
20792     dates:{
20793         en: {
20794             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20795             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20796             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20797             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20798             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20799             today: "Today"
20800         }
20801     },
20802     
20803     modes: [
20804     {
20805         clsName: 'days',
20806         navFnc: 'Month',
20807         navStep: 1
20808     },
20809     {
20810         clsName: 'months',
20811         navFnc: 'FullYear',
20812         navStep: 1
20813     },
20814     {
20815         clsName: 'years',
20816         navFnc: 'FullYear',
20817         navStep: 10
20818     }]
20819 });
20820
20821 Roo.apply(Roo.bootstrap.DateField,  {
20822   
20823     template : {
20824         tag: 'div',
20825         cls: 'datepicker dropdown-menu roo-dynamic',
20826         cn: [
20827         {
20828             tag: 'div',
20829             cls: 'datepicker-days',
20830             cn: [
20831             {
20832                 tag: 'table',
20833                 cls: 'table-condensed',
20834                 cn:[
20835                 Roo.bootstrap.DateField.head,
20836                 {
20837                     tag: 'tbody'
20838                 },
20839                 Roo.bootstrap.DateField.footer
20840                 ]
20841             }
20842             ]
20843         },
20844         {
20845             tag: 'div',
20846             cls: 'datepicker-months',
20847             cn: [
20848             {
20849                 tag: 'table',
20850                 cls: 'table-condensed',
20851                 cn:[
20852                 Roo.bootstrap.DateField.head,
20853                 Roo.bootstrap.DateField.content,
20854                 Roo.bootstrap.DateField.footer
20855                 ]
20856             }
20857             ]
20858         },
20859         {
20860             tag: 'div',
20861             cls: 'datepicker-years',
20862             cn: [
20863             {
20864                 tag: 'table',
20865                 cls: 'table-condensed',
20866                 cn:[
20867                 Roo.bootstrap.DateField.head,
20868                 Roo.bootstrap.DateField.content,
20869                 Roo.bootstrap.DateField.footer
20870                 ]
20871             }
20872             ]
20873         }
20874         ]
20875     }
20876 });
20877
20878  
20879
20880  /*
20881  * - LGPL
20882  *
20883  * TimeField
20884  * 
20885  */
20886
20887 /**
20888  * @class Roo.bootstrap.TimeField
20889  * @extends Roo.bootstrap.Input
20890  * Bootstrap DateField class
20891  * 
20892  * 
20893  * @constructor
20894  * Create a new TimeField
20895  * @param {Object} config The config object
20896  */
20897
20898 Roo.bootstrap.TimeField = function(config){
20899     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20900     this.addEvents({
20901             /**
20902              * @event show
20903              * Fires when this field show.
20904              * @param {Roo.bootstrap.DateField} thisthis
20905              * @param {Mixed} date The date value
20906              */
20907             show : true,
20908             /**
20909              * @event show
20910              * Fires when this field hide.
20911              * @param {Roo.bootstrap.DateField} this
20912              * @param {Mixed} date The date value
20913              */
20914             hide : true,
20915             /**
20916              * @event select
20917              * Fires when select a date.
20918              * @param {Roo.bootstrap.DateField} this
20919              * @param {Mixed} date The date value
20920              */
20921             select : true
20922         });
20923 };
20924
20925 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20926     
20927     /**
20928      * @cfg {String} format
20929      * The default time format string which can be overriden for localization support.  The format must be
20930      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20931      */
20932     format : "H:i",
20933        
20934     onRender: function(ct, position)
20935     {
20936         
20937         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20938                 
20939         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20940         
20941         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20942         
20943         this.pop = this.picker().select('>.datepicker-time',true).first();
20944         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20945         
20946         this.picker().on('mousedown', this.onMousedown, this);
20947         this.picker().on('click', this.onClick, this);
20948         
20949         this.picker().addClass('datepicker-dropdown');
20950     
20951         this.fillTime();
20952         this.update();
20953             
20954         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20955         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20956         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20957         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20958         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20959         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20960
20961     },
20962     
20963     fireKey: function(e){
20964         if (!this.picker().isVisible()){
20965             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20966                 this.show();
20967             }
20968             return;
20969         }
20970
20971         e.preventDefault();
20972         
20973         switch(e.keyCode){
20974             case 27: // escape
20975                 this.hide();
20976                 break;
20977             case 37: // left
20978             case 39: // right
20979                 this.onTogglePeriod();
20980                 break;
20981             case 38: // up
20982                 this.onIncrementMinutes();
20983                 break;
20984             case 40: // down
20985                 this.onDecrementMinutes();
20986                 break;
20987             case 13: // enter
20988             case 9: // tab
20989                 this.setTime();
20990                 break;
20991         }
20992     },
20993     
20994     onClick: function(e) {
20995         e.stopPropagation();
20996         e.preventDefault();
20997     },
20998     
20999     picker : function()
21000     {
21001         return this.el.select('.datepicker', true).first();
21002     },
21003     
21004     fillTime: function()
21005     {    
21006         var time = this.pop.select('tbody', true).first();
21007         
21008         time.dom.innerHTML = '';
21009         
21010         time.createChild({
21011             tag: 'tr',
21012             cn: [
21013                 {
21014                     tag: 'td',
21015                     cn: [
21016                         {
21017                             tag: 'a',
21018                             href: '#',
21019                             cls: 'btn',
21020                             cn: [
21021                                 {
21022                                     tag: 'span',
21023                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21024                                 }
21025                             ]
21026                         } 
21027                     ]
21028                 },
21029                 {
21030                     tag: 'td',
21031                     cls: 'separator'
21032                 },
21033                 {
21034                     tag: 'td',
21035                     cn: [
21036                         {
21037                             tag: 'a',
21038                             href: '#',
21039                             cls: 'btn',
21040                             cn: [
21041                                 {
21042                                     tag: 'span',
21043                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21044                                 }
21045                             ]
21046                         }
21047                     ]
21048                 },
21049                 {
21050                     tag: 'td',
21051                     cls: 'separator'
21052                 }
21053             ]
21054         });
21055         
21056         time.createChild({
21057             tag: 'tr',
21058             cn: [
21059                 {
21060                     tag: 'td',
21061                     cn: [
21062                         {
21063                             tag: 'span',
21064                             cls: 'timepicker-hour',
21065                             html: '00'
21066                         }  
21067                     ]
21068                 },
21069                 {
21070                     tag: 'td',
21071                     cls: 'separator',
21072                     html: ':'
21073                 },
21074                 {
21075                     tag: 'td',
21076                     cn: [
21077                         {
21078                             tag: 'span',
21079                             cls: 'timepicker-minute',
21080                             html: '00'
21081                         }  
21082                     ]
21083                 },
21084                 {
21085                     tag: 'td',
21086                     cls: 'separator'
21087                 },
21088                 {
21089                     tag: 'td',
21090                     cn: [
21091                         {
21092                             tag: 'button',
21093                             type: 'button',
21094                             cls: 'btn btn-primary period',
21095                             html: 'AM'
21096                             
21097                         }
21098                     ]
21099                 }
21100             ]
21101         });
21102         
21103         time.createChild({
21104             tag: 'tr',
21105             cn: [
21106                 {
21107                     tag: 'td',
21108                     cn: [
21109                         {
21110                             tag: 'a',
21111                             href: '#',
21112                             cls: 'btn',
21113                             cn: [
21114                                 {
21115                                     tag: 'span',
21116                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21117                                 }
21118                             ]
21119                         }
21120                     ]
21121                 },
21122                 {
21123                     tag: 'td',
21124                     cls: 'separator'
21125                 },
21126                 {
21127                     tag: 'td',
21128                     cn: [
21129                         {
21130                             tag: 'a',
21131                             href: '#',
21132                             cls: 'btn',
21133                             cn: [
21134                                 {
21135                                     tag: 'span',
21136                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21137                                 }
21138                             ]
21139                         }
21140                     ]
21141                 },
21142                 {
21143                     tag: 'td',
21144                     cls: 'separator'
21145                 }
21146             ]
21147         });
21148         
21149     },
21150     
21151     update: function()
21152     {
21153         
21154         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21155         
21156         this.fill();
21157     },
21158     
21159     fill: function() 
21160     {
21161         var hours = this.time.getHours();
21162         var minutes = this.time.getMinutes();
21163         var period = 'AM';
21164         
21165         if(hours > 11){
21166             period = 'PM';
21167         }
21168         
21169         if(hours == 0){
21170             hours = 12;
21171         }
21172         
21173         
21174         if(hours > 12){
21175             hours = hours - 12;
21176         }
21177         
21178         if(hours < 10){
21179             hours = '0' + hours;
21180         }
21181         
21182         if(minutes < 10){
21183             minutes = '0' + minutes;
21184         }
21185         
21186         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21187         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21188         this.pop.select('button', true).first().dom.innerHTML = period;
21189         
21190     },
21191     
21192     place: function()
21193     {   
21194         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21195         
21196         var cls = ['bottom'];
21197         
21198         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21199             cls.pop();
21200             cls.push('top');
21201         }
21202         
21203         cls.push('right');
21204         
21205         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21206             cls.pop();
21207             cls.push('left');
21208         }
21209         
21210         this.picker().addClass(cls.join('-'));
21211         
21212         var _this = this;
21213         
21214         Roo.each(cls, function(c){
21215             if(c == 'bottom'){
21216                 _this.picker().setTop(_this.inputEl().getHeight());
21217                 return;
21218             }
21219             if(c == 'top'){
21220                 _this.picker().setTop(0 - _this.picker().getHeight());
21221                 return;
21222             }
21223             
21224             if(c == 'left'){
21225                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21226                 return;
21227             }
21228             if(c == 'right'){
21229                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21230                 return;
21231             }
21232         });
21233         
21234     },
21235   
21236     onFocus : function()
21237     {
21238         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21239         this.show();
21240     },
21241     
21242     onBlur : function()
21243     {
21244         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21245         this.hide();
21246     },
21247     
21248     show : function()
21249     {
21250         this.picker().show();
21251         this.pop.show();
21252         this.update();
21253         this.place();
21254         
21255         this.fireEvent('show', this, this.date);
21256     },
21257     
21258     hide : function()
21259     {
21260         this.picker().hide();
21261         this.pop.hide();
21262         
21263         this.fireEvent('hide', this, this.date);
21264     },
21265     
21266     setTime : function()
21267     {
21268         this.hide();
21269         this.setValue(this.time.format(this.format));
21270         
21271         this.fireEvent('select', this, this.date);
21272         
21273         
21274     },
21275     
21276     onMousedown: function(e){
21277         e.stopPropagation();
21278         e.preventDefault();
21279     },
21280     
21281     onIncrementHours: function()
21282     {
21283         Roo.log('onIncrementHours');
21284         this.time = this.time.add(Date.HOUR, 1);
21285         this.update();
21286         
21287     },
21288     
21289     onDecrementHours: function()
21290     {
21291         Roo.log('onDecrementHours');
21292         this.time = this.time.add(Date.HOUR, -1);
21293         this.update();
21294     },
21295     
21296     onIncrementMinutes: function()
21297     {
21298         Roo.log('onIncrementMinutes');
21299         this.time = this.time.add(Date.MINUTE, 1);
21300         this.update();
21301     },
21302     
21303     onDecrementMinutes: function()
21304     {
21305         Roo.log('onDecrementMinutes');
21306         this.time = this.time.add(Date.MINUTE, -1);
21307         this.update();
21308     },
21309     
21310     onTogglePeriod: function()
21311     {
21312         Roo.log('onTogglePeriod');
21313         this.time = this.time.add(Date.HOUR, 12);
21314         this.update();
21315     }
21316     
21317    
21318 });
21319
21320 Roo.apply(Roo.bootstrap.TimeField,  {
21321     
21322     content : {
21323         tag: 'tbody',
21324         cn: [
21325             {
21326                 tag: 'tr',
21327                 cn: [
21328                 {
21329                     tag: 'td',
21330                     colspan: '7'
21331                 }
21332                 ]
21333             }
21334         ]
21335     },
21336     
21337     footer : {
21338         tag: 'tfoot',
21339         cn: [
21340             {
21341                 tag: 'tr',
21342                 cn: [
21343                 {
21344                     tag: 'th',
21345                     colspan: '7',
21346                     cls: '',
21347                     cn: [
21348                         {
21349                             tag: 'button',
21350                             cls: 'btn btn-info ok',
21351                             html: 'OK'
21352                         }
21353                     ]
21354                 }
21355
21356                 ]
21357             }
21358         ]
21359     }
21360 });
21361
21362 Roo.apply(Roo.bootstrap.TimeField,  {
21363   
21364     template : {
21365         tag: 'div',
21366         cls: 'datepicker dropdown-menu',
21367         cn: [
21368             {
21369                 tag: 'div',
21370                 cls: 'datepicker-time',
21371                 cn: [
21372                 {
21373                     tag: 'table',
21374                     cls: 'table-condensed',
21375                     cn:[
21376                     Roo.bootstrap.TimeField.content,
21377                     Roo.bootstrap.TimeField.footer
21378                     ]
21379                 }
21380                 ]
21381             }
21382         ]
21383     }
21384 });
21385
21386  
21387
21388  /*
21389  * - LGPL
21390  *
21391  * MonthField
21392  * 
21393  */
21394
21395 /**
21396  * @class Roo.bootstrap.MonthField
21397  * @extends Roo.bootstrap.Input
21398  * Bootstrap MonthField class
21399  * 
21400  * @cfg {String} language default en
21401  * 
21402  * @constructor
21403  * Create a new MonthField
21404  * @param {Object} config The config object
21405  */
21406
21407 Roo.bootstrap.MonthField = function(config){
21408     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21409     
21410     this.addEvents({
21411         /**
21412          * @event show
21413          * Fires when this field show.
21414          * @param {Roo.bootstrap.MonthField} this
21415          * @param {Mixed} date The date value
21416          */
21417         show : true,
21418         /**
21419          * @event show
21420          * Fires when this field hide.
21421          * @param {Roo.bootstrap.MonthField} this
21422          * @param {Mixed} date The date value
21423          */
21424         hide : true,
21425         /**
21426          * @event select
21427          * Fires when select a date.
21428          * @param {Roo.bootstrap.MonthField} this
21429          * @param {String} oldvalue The old value
21430          * @param {String} newvalue The new value
21431          */
21432         select : true
21433     });
21434 };
21435
21436 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21437     
21438     onRender: function(ct, position)
21439     {
21440         
21441         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21442         
21443         this.language = this.language || 'en';
21444         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21445         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21446         
21447         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21448         this.isInline = false;
21449         this.isInput = true;
21450         this.component = this.el.select('.add-on', true).first() || false;
21451         this.component = (this.component && this.component.length === 0) ? false : this.component;
21452         this.hasInput = this.component && this.inputEL().length;
21453         
21454         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21455         
21456         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21457         
21458         this.picker().on('mousedown', this.onMousedown, this);
21459         this.picker().on('click', this.onClick, this);
21460         
21461         this.picker().addClass('datepicker-dropdown');
21462         
21463         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21464             v.setStyle('width', '189px');
21465         });
21466         
21467         this.fillMonths();
21468         
21469         this.update();
21470         
21471         if(this.isInline) {
21472             this.show();
21473         }
21474         
21475     },
21476     
21477     setValue: function(v, suppressEvent)
21478     {   
21479         var o = this.getValue();
21480         
21481         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21482         
21483         this.update();
21484
21485         if(suppressEvent !== true){
21486             this.fireEvent('select', this, o, v);
21487         }
21488         
21489     },
21490     
21491     getValue: function()
21492     {
21493         return this.value;
21494     },
21495     
21496     onClick: function(e) 
21497     {
21498         e.stopPropagation();
21499         e.preventDefault();
21500         
21501         var target = e.getTarget();
21502         
21503         if(target.nodeName.toLowerCase() === 'i'){
21504             target = Roo.get(target).dom.parentNode;
21505         }
21506         
21507         var nodeName = target.nodeName;
21508         var className = target.className;
21509         var html = target.innerHTML;
21510         
21511         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21512             return;
21513         }
21514         
21515         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21516         
21517         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21518         
21519         this.hide();
21520                         
21521     },
21522     
21523     picker : function()
21524     {
21525         return this.pickerEl;
21526     },
21527     
21528     fillMonths: function()
21529     {    
21530         var i = 0;
21531         var months = this.picker().select('>.datepicker-months td', true).first();
21532         
21533         months.dom.innerHTML = '';
21534         
21535         while (i < 12) {
21536             var month = {
21537                 tag: 'span',
21538                 cls: 'month',
21539                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21540             };
21541             
21542             months.createChild(month);
21543         }
21544         
21545     },
21546     
21547     update: function()
21548     {
21549         var _this = this;
21550         
21551         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21552             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21553         }
21554         
21555         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21556             e.removeClass('active');
21557             
21558             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21559                 e.addClass('active');
21560             }
21561         })
21562     },
21563     
21564     place: function()
21565     {
21566         if(this.isInline) {
21567             return;
21568         }
21569         
21570         this.picker().removeClass(['bottom', 'top']);
21571         
21572         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21573             /*
21574              * place to the top of element!
21575              *
21576              */
21577             
21578             this.picker().addClass('top');
21579             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21580             
21581             return;
21582         }
21583         
21584         this.picker().addClass('bottom');
21585         
21586         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21587     },
21588     
21589     onFocus : function()
21590     {
21591         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21592         this.show();
21593     },
21594     
21595     onBlur : function()
21596     {
21597         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21598         
21599         var d = this.inputEl().getValue();
21600         
21601         this.setValue(d);
21602                 
21603         this.hide();
21604     },
21605     
21606     show : function()
21607     {
21608         this.picker().show();
21609         this.picker().select('>.datepicker-months', true).first().show();
21610         this.update();
21611         this.place();
21612         
21613         this.fireEvent('show', this, this.date);
21614     },
21615     
21616     hide : function()
21617     {
21618         if(this.isInline) {
21619             return;
21620         }
21621         this.picker().hide();
21622         this.fireEvent('hide', this, this.date);
21623         
21624     },
21625     
21626     onMousedown: function(e)
21627     {
21628         e.stopPropagation();
21629         e.preventDefault();
21630     },
21631     
21632     keyup: function(e)
21633     {
21634         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21635         this.update();
21636     },
21637
21638     fireKey: function(e)
21639     {
21640         if (!this.picker().isVisible()){
21641             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21642                 this.show();
21643             }
21644             return;
21645         }
21646         
21647         var dir;
21648         
21649         switch(e.keyCode){
21650             case 27: // escape
21651                 this.hide();
21652                 e.preventDefault();
21653                 break;
21654             case 37: // left
21655             case 39: // right
21656                 dir = e.keyCode == 37 ? -1 : 1;
21657                 
21658                 this.vIndex = this.vIndex + dir;
21659                 
21660                 if(this.vIndex < 0){
21661                     this.vIndex = 0;
21662                 }
21663                 
21664                 if(this.vIndex > 11){
21665                     this.vIndex = 11;
21666                 }
21667                 
21668                 if(isNaN(this.vIndex)){
21669                     this.vIndex = 0;
21670                 }
21671                 
21672                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21673                 
21674                 break;
21675             case 38: // up
21676             case 40: // down
21677                 
21678                 dir = e.keyCode == 38 ? -1 : 1;
21679                 
21680                 this.vIndex = this.vIndex + dir * 4;
21681                 
21682                 if(this.vIndex < 0){
21683                     this.vIndex = 0;
21684                 }
21685                 
21686                 if(this.vIndex > 11){
21687                     this.vIndex = 11;
21688                 }
21689                 
21690                 if(isNaN(this.vIndex)){
21691                     this.vIndex = 0;
21692                 }
21693                 
21694                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21695                 break;
21696                 
21697             case 13: // enter
21698                 
21699                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21700                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21701                 }
21702                 
21703                 this.hide();
21704                 e.preventDefault();
21705                 break;
21706             case 9: // tab
21707                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21708                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21709                 }
21710                 this.hide();
21711                 break;
21712             case 16: // shift
21713             case 17: // ctrl
21714             case 18: // alt
21715                 break;
21716             default :
21717                 this.hide();
21718                 
21719         }
21720     },
21721     
21722     remove: function() 
21723     {
21724         this.picker().remove();
21725     }
21726    
21727 });
21728
21729 Roo.apply(Roo.bootstrap.MonthField,  {
21730     
21731     content : {
21732         tag: 'tbody',
21733         cn: [
21734         {
21735             tag: 'tr',
21736             cn: [
21737             {
21738                 tag: 'td',
21739                 colspan: '7'
21740             }
21741             ]
21742         }
21743         ]
21744     },
21745     
21746     dates:{
21747         en: {
21748             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21749             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21750         }
21751     }
21752 });
21753
21754 Roo.apply(Roo.bootstrap.MonthField,  {
21755   
21756     template : {
21757         tag: 'div',
21758         cls: 'datepicker dropdown-menu roo-dynamic',
21759         cn: [
21760             {
21761                 tag: 'div',
21762                 cls: 'datepicker-months',
21763                 cn: [
21764                 {
21765                     tag: 'table',
21766                     cls: 'table-condensed',
21767                     cn:[
21768                         Roo.bootstrap.DateField.content
21769                     ]
21770                 }
21771                 ]
21772             }
21773         ]
21774     }
21775 });
21776
21777  
21778
21779  
21780  /*
21781  * - LGPL
21782  *
21783  * CheckBox
21784  * 
21785  */
21786
21787 /**
21788  * @class Roo.bootstrap.CheckBox
21789  * @extends Roo.bootstrap.Input
21790  * Bootstrap CheckBox class
21791  * 
21792  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21793  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21794  * @cfg {String} boxLabel The text that appears beside the checkbox
21795  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21796  * @cfg {Boolean} checked initnal the element
21797  * @cfg {Boolean} inline inline the element (default false)
21798  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21799  * @cfg {String} tooltip label tooltip
21800  * 
21801  * @constructor
21802  * Create a new CheckBox
21803  * @param {Object} config The config object
21804  */
21805
21806 Roo.bootstrap.CheckBox = function(config){
21807     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21808    
21809     this.addEvents({
21810         /**
21811         * @event check
21812         * Fires when the element is checked or unchecked.
21813         * @param {Roo.bootstrap.CheckBox} this This input
21814         * @param {Boolean} checked The new checked value
21815         */
21816        check : true,
21817        /**
21818         * @event click
21819         * Fires when the element is click.
21820         * @param {Roo.bootstrap.CheckBox} this This input
21821         */
21822        click : true
21823     });
21824     
21825 };
21826
21827 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21828   
21829     inputType: 'checkbox',
21830     inputValue: 1,
21831     valueOff: 0,
21832     boxLabel: false,
21833     checked: false,
21834     weight : false,
21835     inline: false,
21836     tooltip : '',
21837     
21838     // checkbox success does not make any sense really.. 
21839     invalidClass : "",
21840     validClass : "",
21841     
21842     
21843     getAutoCreate : function()
21844     {
21845         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21846         
21847         var id = Roo.id();
21848         
21849         var cfg = {};
21850         
21851         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21852         
21853         if(this.inline){
21854             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21855         }
21856         
21857         var input =  {
21858             tag: 'input',
21859             id : id,
21860             type : this.inputType,
21861             value : this.inputValue,
21862             cls : 'roo-' + this.inputType, //'form-box',
21863             placeholder : this.placeholder || ''
21864             
21865         };
21866         
21867         if(this.inputType != 'radio'){
21868             var hidden =  {
21869                 tag: 'input',
21870                 type : 'hidden',
21871                 cls : 'roo-hidden-value',
21872                 value : this.checked ? this.inputValue : this.valueOff
21873             };
21874         }
21875         
21876             
21877         if (this.weight) { // Validity check?
21878             cfg.cls += " " + this.inputType + "-" + this.weight;
21879         }
21880         
21881         if (this.disabled) {
21882             input.disabled=true;
21883         }
21884         
21885         if(this.checked){
21886             input.checked = this.checked;
21887         }
21888         
21889         if (this.name) {
21890             
21891             input.name = this.name;
21892             
21893             if(this.inputType != 'radio'){
21894                 hidden.name = this.name;
21895                 input.name = '_hidden_' + this.name;
21896             }
21897         }
21898         
21899         if (this.size) {
21900             input.cls += ' input-' + this.size;
21901         }
21902         
21903         var settings=this;
21904         
21905         ['xs','sm','md','lg'].map(function(size){
21906             if (settings[size]) {
21907                 cfg.cls += ' col-' + size + '-' + settings[size];
21908             }
21909         });
21910         
21911         var inputblock = input;
21912          
21913         if (this.before || this.after) {
21914             
21915             inputblock = {
21916                 cls : 'input-group',
21917                 cn :  [] 
21918             };
21919             
21920             if (this.before) {
21921                 inputblock.cn.push({
21922                     tag :'span',
21923                     cls : 'input-group-addon',
21924                     html : this.before
21925                 });
21926             }
21927             
21928             inputblock.cn.push(input);
21929             
21930             if(this.inputType != 'radio'){
21931                 inputblock.cn.push(hidden);
21932             }
21933             
21934             if (this.after) {
21935                 inputblock.cn.push({
21936                     tag :'span',
21937                     cls : 'input-group-addon',
21938                     html : this.after
21939                 });
21940             }
21941             
21942         }
21943         var boxLabelCfg = false;
21944         
21945         if(this.boxLabel){
21946            
21947             boxLabelCfg = {
21948                 tag: 'label',
21949                 //'for': id, // box label is handled by onclick - so no for...
21950                 cls: 'box-label',
21951                 html: this.boxLabel
21952             };
21953             if(this.tooltip){
21954                 boxLabelCfg.tooltip = this.tooltip;
21955             }
21956              
21957         }
21958         
21959         
21960         if (align ==='left' && this.fieldLabel.length) {
21961 //                Roo.log("left and has label");
21962             cfg.cn = [
21963                 {
21964                     tag: 'label',
21965                     'for' :  id,
21966                     cls : 'control-label',
21967                     html : this.fieldLabel
21968                 },
21969                 {
21970                     cls : "", 
21971                     cn: [
21972                         inputblock
21973                     ]
21974                 }
21975             ];
21976             
21977             if (boxLabelCfg) {
21978                 cfg.cn[1].cn.push(boxLabelCfg);
21979             }
21980             
21981             if(this.labelWidth > 12){
21982                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21983             }
21984             
21985             if(this.labelWidth < 13 && this.labelmd == 0){
21986                 this.labelmd = this.labelWidth;
21987             }
21988             
21989             if(this.labellg > 0){
21990                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21991                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21992             }
21993             
21994             if(this.labelmd > 0){
21995                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21996                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21997             }
21998             
21999             if(this.labelsm > 0){
22000                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22001                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22002             }
22003             
22004             if(this.labelxs > 0){
22005                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22006                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22007             }
22008             
22009         } else if ( this.fieldLabel.length) {
22010 //                Roo.log(" label");
22011                 cfg.cn = [
22012                    
22013                     {
22014                         tag: this.boxLabel ? 'span' : 'label',
22015                         'for': id,
22016                         cls: 'control-label box-input-label',
22017                         //cls : 'input-group-addon',
22018                         html : this.fieldLabel
22019                     },
22020                     
22021                     inputblock
22022                     
22023                 ];
22024                 if (boxLabelCfg) {
22025                     cfg.cn.push(boxLabelCfg);
22026                 }
22027
22028         } else {
22029             
22030 //                Roo.log(" no label && no align");
22031                 cfg.cn = [  inputblock ] ;
22032                 if (boxLabelCfg) {
22033                     cfg.cn.push(boxLabelCfg);
22034                 }
22035
22036                 
22037         }
22038         
22039        
22040         
22041         if(this.inputType != 'radio'){
22042             cfg.cn.push(hidden);
22043         }
22044         
22045         return cfg;
22046         
22047     },
22048     
22049     /**
22050      * return the real input element.
22051      */
22052     inputEl: function ()
22053     {
22054         return this.el.select('input.roo-' + this.inputType,true).first();
22055     },
22056     hiddenEl: function ()
22057     {
22058         return this.el.select('input.roo-hidden-value',true).first();
22059     },
22060     
22061     labelEl: function()
22062     {
22063         return this.el.select('label.control-label',true).first();
22064     },
22065     /* depricated... */
22066     
22067     label: function()
22068     {
22069         return this.labelEl();
22070     },
22071     
22072     boxLabelEl: function()
22073     {
22074         return this.el.select('label.box-label',true).first();
22075     },
22076     
22077     initEvents : function()
22078     {
22079 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22080         
22081         this.inputEl().on('click', this.onClick,  this);
22082         
22083         if (this.boxLabel) { 
22084             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22085         }
22086         
22087         this.startValue = this.getValue();
22088         
22089         if(this.groupId){
22090             Roo.bootstrap.CheckBox.register(this);
22091         }
22092     },
22093     
22094     onClick : function(e)
22095     {   
22096         if(this.fireEvent('click', this, e) !== false){
22097             this.setChecked(!this.checked);
22098         }
22099         
22100     },
22101     
22102     setChecked : function(state,suppressEvent)
22103     {
22104         this.startValue = this.getValue();
22105
22106         if(this.inputType == 'radio'){
22107             
22108             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22109                 e.dom.checked = false;
22110             });
22111             
22112             this.inputEl().dom.checked = true;
22113             
22114             this.inputEl().dom.value = this.inputValue;
22115             
22116             if(suppressEvent !== true){
22117                 this.fireEvent('check', this, true);
22118             }
22119             
22120             this.validate();
22121             
22122             return;
22123         }
22124         
22125         this.checked = state;
22126         
22127         this.inputEl().dom.checked = state;
22128         
22129         
22130         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22131         
22132         if(suppressEvent !== true){
22133             this.fireEvent('check', this, state);
22134         }
22135         
22136         this.validate();
22137     },
22138     
22139     getValue : function()
22140     {
22141         if(this.inputType == 'radio'){
22142             return this.getGroupValue();
22143         }
22144         
22145         return this.hiddenEl().dom.value;
22146         
22147     },
22148     
22149     getGroupValue : function()
22150     {
22151         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22152             return '';
22153         }
22154         
22155         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22156     },
22157     
22158     setValue : function(v,suppressEvent)
22159     {
22160         if(this.inputType == 'radio'){
22161             this.setGroupValue(v, suppressEvent);
22162             return;
22163         }
22164         
22165         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22166         
22167         this.validate();
22168     },
22169     
22170     setGroupValue : function(v, suppressEvent)
22171     {
22172         this.startValue = this.getValue();
22173         
22174         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22175             e.dom.checked = false;
22176             
22177             if(e.dom.value == v){
22178                 e.dom.checked = true;
22179             }
22180         });
22181         
22182         if(suppressEvent !== true){
22183             this.fireEvent('check', this, true);
22184         }
22185
22186         this.validate();
22187         
22188         return;
22189     },
22190     
22191     validate : function()
22192     {
22193         if(this.getVisibilityEl().hasClass('hidden')){
22194             return true;
22195         }
22196         
22197         if(
22198                 this.disabled || 
22199                 (this.inputType == 'radio' && this.validateRadio()) ||
22200                 (this.inputType == 'checkbox' && this.validateCheckbox())
22201         ){
22202             this.markValid();
22203             return true;
22204         }
22205         
22206         this.markInvalid();
22207         return false;
22208     },
22209     
22210     validateRadio : function()
22211     {
22212         if(this.getVisibilityEl().hasClass('hidden')){
22213             return true;
22214         }
22215         
22216         if(this.allowBlank){
22217             return true;
22218         }
22219         
22220         var valid = false;
22221         
22222         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22223             if(!e.dom.checked){
22224                 return;
22225             }
22226             
22227             valid = true;
22228             
22229             return false;
22230         });
22231         
22232         return valid;
22233     },
22234     
22235     validateCheckbox : function()
22236     {
22237         if(!this.groupId){
22238             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22239             //return (this.getValue() == this.inputValue) ? true : false;
22240         }
22241         
22242         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22243         
22244         if(!group){
22245             return false;
22246         }
22247         
22248         var r = false;
22249         
22250         for(var i in group){
22251             if(group[i].el.isVisible(true)){
22252                 r = false;
22253                 break;
22254             }
22255             
22256             r = true;
22257         }
22258         
22259         for(var i in group){
22260             if(r){
22261                 break;
22262             }
22263             
22264             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22265         }
22266         
22267         return r;
22268     },
22269     
22270     /**
22271      * Mark this field as valid
22272      */
22273     markValid : function()
22274     {
22275         var _this = this;
22276         
22277         this.fireEvent('valid', this);
22278         
22279         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22280         
22281         if(this.groupId){
22282             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22283         }
22284         
22285         if(label){
22286             label.markValid();
22287         }
22288
22289         if(this.inputType == 'radio'){
22290             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22291                 var fg = e.findParent('.form-group', false, true);
22292                 if (Roo.bootstrap.version == 3) {
22293                     fg.removeClass([_this.invalidClass, _this.validClass]);
22294                     fg.addClass(_this.validClass);
22295                 } else {
22296                     fg.removeClass(['is-valid', 'is-invalid']);
22297                     fg.addClass('is-valid');
22298                 }
22299             });
22300             
22301             return;
22302         }
22303
22304         if(!this.groupId){
22305             var fg = this.el.findParent('.form-group', false, true);
22306             if (Roo.bootstrap.version == 3) {
22307                 fg.removeClass([this.invalidClass, this.validClass]);
22308                 fg.addClass(this.validClass);
22309             } else {
22310                 fg.removeClass(['is-valid', 'is-invalid']);
22311                 fg.addClass('is-valid');
22312             }
22313             return;
22314         }
22315         
22316         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22317         
22318         if(!group){
22319             return;
22320         }
22321         
22322         for(var i in group){
22323             var fg = group[i].el.findParent('.form-group', false, true);
22324             if (Roo.bootstrap.version == 3) {
22325                 fg.removeClass([this.invalidClass, this.validClass]);
22326                 fg.addClass(this.validClass);
22327             } else {
22328                 fg.removeClass(['is-valid', 'is-invalid']);
22329                 fg.addClass('is-valid');
22330             }
22331         }
22332     },
22333     
22334      /**
22335      * Mark this field as invalid
22336      * @param {String} msg The validation message
22337      */
22338     markInvalid : function(msg)
22339     {
22340         if(this.allowBlank){
22341             return;
22342         }
22343         
22344         var _this = this;
22345         
22346         this.fireEvent('invalid', this, msg);
22347         
22348         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22349         
22350         if(this.groupId){
22351             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22352         }
22353         
22354         if(label){
22355             label.markInvalid();
22356         }
22357             
22358         if(this.inputType == 'radio'){
22359             
22360             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22361                 var fg = e.findParent('.form-group', false, true);
22362                 if (Roo.bootstrap.version == 3) {
22363                     fg.removeClass([_this.invalidClass, _this.validClass]);
22364                     fg.addClass(_this.invalidClass);
22365                 } else {
22366                     fg.removeClass(['is-invalid', 'is-valid']);
22367                     fg.addClass('is-invalid');
22368                 }
22369             });
22370             
22371             return;
22372         }
22373         
22374         if(!this.groupId){
22375             var fg = this.el.findParent('.form-group', false, true);
22376             if (Roo.bootstrap.version == 3) {
22377                 fg.removeClass([_this.invalidClass, _this.validClass]);
22378                 fg.addClass(_this.invalidClass);
22379             } else {
22380                 fg.removeClass(['is-invalid', 'is-valid']);
22381                 fg.addClass('is-invalid');
22382             }
22383             return;
22384         }
22385         
22386         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22387         
22388         if(!group){
22389             return;
22390         }
22391         
22392         for(var i in group){
22393             var fg = group[i].el.findParent('.form-group', false, true);
22394             if (Roo.bootstrap.version == 3) {
22395                 fg.removeClass([_this.invalidClass, _this.validClass]);
22396                 fg.addClass(_this.invalidClass);
22397             } else {
22398                 fg.removeClass(['is-invalid', 'is-valid']);
22399                 fg.addClass('is-invalid');
22400             }
22401         }
22402         
22403     },
22404     
22405     clearInvalid : function()
22406     {
22407         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22408         
22409         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22410         
22411         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22412         
22413         if (label && label.iconEl) {
22414             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22415             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22416         }
22417     },
22418     
22419     disable : function()
22420     {
22421         if(this.inputType != 'radio'){
22422             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22423             return;
22424         }
22425         
22426         var _this = this;
22427         
22428         if(this.rendered){
22429             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22430                 _this.getActionEl().addClass(this.disabledClass);
22431                 e.dom.disabled = true;
22432             });
22433         }
22434         
22435         this.disabled = true;
22436         this.fireEvent("disable", this);
22437         return this;
22438     },
22439
22440     enable : function()
22441     {
22442         if(this.inputType != 'radio'){
22443             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22444             return;
22445         }
22446         
22447         var _this = this;
22448         
22449         if(this.rendered){
22450             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22451                 _this.getActionEl().removeClass(this.disabledClass);
22452                 e.dom.disabled = false;
22453             });
22454         }
22455         
22456         this.disabled = false;
22457         this.fireEvent("enable", this);
22458         return this;
22459     },
22460     
22461     setBoxLabel : function(v)
22462     {
22463         this.boxLabel = v;
22464         
22465         if(this.rendered){
22466             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22467         }
22468     }
22469
22470 });
22471
22472 Roo.apply(Roo.bootstrap.CheckBox, {
22473     
22474     groups: {},
22475     
22476      /**
22477     * register a CheckBox Group
22478     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22479     */
22480     register : function(checkbox)
22481     {
22482         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22483             this.groups[checkbox.groupId] = {};
22484         }
22485         
22486         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22487             return;
22488         }
22489         
22490         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22491         
22492     },
22493     /**
22494     * fetch a CheckBox Group based on the group ID
22495     * @param {string} the group ID
22496     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22497     */
22498     get: function(groupId) {
22499         if (typeof(this.groups[groupId]) == 'undefined') {
22500             return false;
22501         }
22502         
22503         return this.groups[groupId] ;
22504     }
22505     
22506     
22507 });
22508 /*
22509  * - LGPL
22510  *
22511  * RadioItem
22512  * 
22513  */
22514
22515 /**
22516  * @class Roo.bootstrap.Radio
22517  * @extends Roo.bootstrap.Component
22518  * Bootstrap Radio class
22519  * @cfg {String} boxLabel - the label associated
22520  * @cfg {String} value - the value of radio
22521  * 
22522  * @constructor
22523  * Create a new Radio
22524  * @param {Object} config The config object
22525  */
22526 Roo.bootstrap.Radio = function(config){
22527     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22528     
22529 };
22530
22531 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22532     
22533     boxLabel : '',
22534     
22535     value : '',
22536     
22537     getAutoCreate : function()
22538     {
22539         var cfg = {
22540             tag : 'div',
22541             cls : 'form-group radio',
22542             cn : [
22543                 {
22544                     tag : 'label',
22545                     cls : 'box-label',
22546                     html : this.boxLabel
22547                 }
22548             ]
22549         };
22550         
22551         return cfg;
22552     },
22553     
22554     initEvents : function() 
22555     {
22556         this.parent().register(this);
22557         
22558         this.el.on('click', this.onClick, this);
22559         
22560     },
22561     
22562     onClick : function(e)
22563     {
22564         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22565             this.setChecked(true);
22566         }
22567     },
22568     
22569     setChecked : function(state, suppressEvent)
22570     {
22571         this.parent().setValue(this.value, suppressEvent);
22572         
22573     },
22574     
22575     setBoxLabel : function(v)
22576     {
22577         this.boxLabel = v;
22578         
22579         if(this.rendered){
22580             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22581         }
22582     }
22583     
22584 });
22585  
22586
22587  /*
22588  * - LGPL
22589  *
22590  * Input
22591  * 
22592  */
22593
22594 /**
22595  * @class Roo.bootstrap.SecurePass
22596  * @extends Roo.bootstrap.Input
22597  * Bootstrap SecurePass class
22598  *
22599  * 
22600  * @constructor
22601  * Create a new SecurePass
22602  * @param {Object} config The config object
22603  */
22604  
22605 Roo.bootstrap.SecurePass = function (config) {
22606     // these go here, so the translation tool can replace them..
22607     this.errors = {
22608         PwdEmpty: "Please type a password, and then retype it to confirm.",
22609         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22610         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22611         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22612         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22613         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22614         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22615         TooWeak: "Your password is Too Weak."
22616     },
22617     this.meterLabel = "Password strength:";
22618     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22619     this.meterClass = [
22620         "roo-password-meter-tooweak", 
22621         "roo-password-meter-weak", 
22622         "roo-password-meter-medium", 
22623         "roo-password-meter-strong", 
22624         "roo-password-meter-grey"
22625     ];
22626     
22627     this.errors = {};
22628     
22629     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22630 }
22631
22632 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22633     /**
22634      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22635      * {
22636      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22637      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22638      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22639      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22640      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22641      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22642      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22643      * })
22644      */
22645     // private
22646     
22647     meterWidth: 300,
22648     errorMsg :'',    
22649     errors: false,
22650     imageRoot: '/',
22651     /**
22652      * @cfg {String/Object} Label for the strength meter (defaults to
22653      * 'Password strength:')
22654      */
22655     // private
22656     meterLabel: '',
22657     /**
22658      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22659      * ['Weak', 'Medium', 'Strong'])
22660      */
22661     // private    
22662     pwdStrengths: false,    
22663     // private
22664     strength: 0,
22665     // private
22666     _lastPwd: null,
22667     // private
22668     kCapitalLetter: 0,
22669     kSmallLetter: 1,
22670     kDigit: 2,
22671     kPunctuation: 3,
22672     
22673     insecure: false,
22674     // private
22675     initEvents: function ()
22676     {
22677         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22678
22679         if (this.el.is('input[type=password]') && Roo.isSafari) {
22680             this.el.on('keydown', this.SafariOnKeyDown, this);
22681         }
22682
22683         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22684     },
22685     // private
22686     onRender: function (ct, position)
22687     {
22688         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22689         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22690         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22691
22692         this.trigger.createChild({
22693                    cn: [
22694                     {
22695                     //id: 'PwdMeter',
22696                     tag: 'div',
22697                     cls: 'roo-password-meter-grey col-xs-12',
22698                     style: {
22699                         //width: 0,
22700                         //width: this.meterWidth + 'px'                                                
22701                         }
22702                     },
22703                     {                            
22704                          cls: 'roo-password-meter-text'                          
22705                     }
22706                 ]            
22707         });
22708
22709          
22710         if (this.hideTrigger) {
22711             this.trigger.setDisplayed(false);
22712         }
22713         this.setSize(this.width || '', this.height || '');
22714     },
22715     // private
22716     onDestroy: function ()
22717     {
22718         if (this.trigger) {
22719             this.trigger.removeAllListeners();
22720             this.trigger.remove();
22721         }
22722         if (this.wrap) {
22723             this.wrap.remove();
22724         }
22725         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22726     },
22727     // private
22728     checkStrength: function ()
22729     {
22730         var pwd = this.inputEl().getValue();
22731         if (pwd == this._lastPwd) {
22732             return;
22733         }
22734
22735         var strength;
22736         if (this.ClientSideStrongPassword(pwd)) {
22737             strength = 3;
22738         } else if (this.ClientSideMediumPassword(pwd)) {
22739             strength = 2;
22740         } else if (this.ClientSideWeakPassword(pwd)) {
22741             strength = 1;
22742         } else {
22743             strength = 0;
22744         }
22745         
22746         Roo.log('strength1: ' + strength);
22747         
22748         //var pm = this.trigger.child('div/div/div').dom;
22749         var pm = this.trigger.child('div/div');
22750         pm.removeClass(this.meterClass);
22751         pm.addClass(this.meterClass[strength]);
22752                 
22753         
22754         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22755                 
22756         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22757         
22758         this._lastPwd = pwd;
22759     },
22760     reset: function ()
22761     {
22762         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22763         
22764         this._lastPwd = '';
22765         
22766         var pm = this.trigger.child('div/div');
22767         pm.removeClass(this.meterClass);
22768         pm.addClass('roo-password-meter-grey');        
22769         
22770         
22771         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22772         
22773         pt.innerHTML = '';
22774         this.inputEl().dom.type='password';
22775     },
22776     // private
22777     validateValue: function (value)
22778     {
22779         
22780         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22781             return false;
22782         }
22783         if (value.length == 0) {
22784             if (this.allowBlank) {
22785                 this.clearInvalid();
22786                 return true;
22787             }
22788
22789             this.markInvalid(this.errors.PwdEmpty);
22790             this.errorMsg = this.errors.PwdEmpty;
22791             return false;
22792         }
22793         
22794         if(this.insecure){
22795             return true;
22796         }
22797         
22798         if ('[\x21-\x7e]*'.match(value)) {
22799             this.markInvalid(this.errors.PwdBadChar);
22800             this.errorMsg = this.errors.PwdBadChar;
22801             return false;
22802         }
22803         if (value.length < 6) {
22804             this.markInvalid(this.errors.PwdShort);
22805             this.errorMsg = this.errors.PwdShort;
22806             return false;
22807         }
22808         if (value.length > 16) {
22809             this.markInvalid(this.errors.PwdLong);
22810             this.errorMsg = this.errors.PwdLong;
22811             return false;
22812         }
22813         var strength;
22814         if (this.ClientSideStrongPassword(value)) {
22815             strength = 3;
22816         } else if (this.ClientSideMediumPassword(value)) {
22817             strength = 2;
22818         } else if (this.ClientSideWeakPassword(value)) {
22819             strength = 1;
22820         } else {
22821             strength = 0;
22822         }
22823
22824         
22825         if (strength < 2) {
22826             //this.markInvalid(this.errors.TooWeak);
22827             this.errorMsg = this.errors.TooWeak;
22828             //return false;
22829         }
22830         
22831         
22832         console.log('strength2: ' + strength);
22833         
22834         //var pm = this.trigger.child('div/div/div').dom;
22835         
22836         var pm = this.trigger.child('div/div');
22837         pm.removeClass(this.meterClass);
22838         pm.addClass(this.meterClass[strength]);
22839                 
22840         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22841                 
22842         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22843         
22844         this.errorMsg = ''; 
22845         return true;
22846     },
22847     // private
22848     CharacterSetChecks: function (type)
22849     {
22850         this.type = type;
22851         this.fResult = false;
22852     },
22853     // private
22854     isctype: function (character, type)
22855     {
22856         switch (type) {  
22857             case this.kCapitalLetter:
22858                 if (character >= 'A' && character <= 'Z') {
22859                     return true;
22860                 }
22861                 break;
22862             
22863             case this.kSmallLetter:
22864                 if (character >= 'a' && character <= 'z') {
22865                     return true;
22866                 }
22867                 break;
22868             
22869             case this.kDigit:
22870                 if (character >= '0' && character <= '9') {
22871                     return true;
22872                 }
22873                 break;
22874             
22875             case this.kPunctuation:
22876                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22877                     return true;
22878                 }
22879                 break;
22880             
22881             default:
22882                 return false;
22883         }
22884
22885     },
22886     // private
22887     IsLongEnough: function (pwd, size)
22888     {
22889         return !(pwd == null || isNaN(size) || pwd.length < size);
22890     },
22891     // private
22892     SpansEnoughCharacterSets: function (word, nb)
22893     {
22894         if (!this.IsLongEnough(word, nb))
22895         {
22896             return false;
22897         }
22898
22899         var characterSetChecks = new Array(
22900             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22901             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22902         );
22903         
22904         for (var index = 0; index < word.length; ++index) {
22905             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22906                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22907                     characterSetChecks[nCharSet].fResult = true;
22908                     break;
22909                 }
22910             }
22911         }
22912
22913         var nCharSets = 0;
22914         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22915             if (characterSetChecks[nCharSet].fResult) {
22916                 ++nCharSets;
22917             }
22918         }
22919
22920         if (nCharSets < nb) {
22921             return false;
22922         }
22923         return true;
22924     },
22925     // private
22926     ClientSideStrongPassword: function (pwd)
22927     {
22928         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22929     },
22930     // private
22931     ClientSideMediumPassword: function (pwd)
22932     {
22933         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22934     },
22935     // private
22936     ClientSideWeakPassword: function (pwd)
22937     {
22938         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22939     }
22940           
22941 })//<script type="text/javascript">
22942
22943 /*
22944  * Based  Ext JS Library 1.1.1
22945  * Copyright(c) 2006-2007, Ext JS, LLC.
22946  * LGPL
22947  *
22948  */
22949  
22950 /**
22951  * @class Roo.HtmlEditorCore
22952  * @extends Roo.Component
22953  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22954  *
22955  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22956  */
22957
22958 Roo.HtmlEditorCore = function(config){
22959     
22960     
22961     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22962     
22963     
22964     this.addEvents({
22965         /**
22966          * @event initialize
22967          * Fires when the editor is fully initialized (including the iframe)
22968          * @param {Roo.HtmlEditorCore} this
22969          */
22970         initialize: true,
22971         /**
22972          * @event activate
22973          * Fires when the editor is first receives the focus. Any insertion must wait
22974          * until after this event.
22975          * @param {Roo.HtmlEditorCore} this
22976          */
22977         activate: true,
22978          /**
22979          * @event beforesync
22980          * Fires before the textarea is updated with content from the editor iframe. Return false
22981          * to cancel the sync.
22982          * @param {Roo.HtmlEditorCore} this
22983          * @param {String} html
22984          */
22985         beforesync: true,
22986          /**
22987          * @event beforepush
22988          * Fires before the iframe editor is updated with content from the textarea. Return false
22989          * to cancel the push.
22990          * @param {Roo.HtmlEditorCore} this
22991          * @param {String} html
22992          */
22993         beforepush: true,
22994          /**
22995          * @event sync
22996          * Fires when the textarea is updated with content from the editor iframe.
22997          * @param {Roo.HtmlEditorCore} this
22998          * @param {String} html
22999          */
23000         sync: true,
23001          /**
23002          * @event push
23003          * Fires when the iframe editor is updated with content from the textarea.
23004          * @param {Roo.HtmlEditorCore} this
23005          * @param {String} html
23006          */
23007         push: true,
23008         
23009         /**
23010          * @event editorevent
23011          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23012          * @param {Roo.HtmlEditorCore} this
23013          */
23014         editorevent: true
23015         
23016     });
23017     
23018     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23019     
23020     // defaults : white / black...
23021     this.applyBlacklists();
23022     
23023     
23024     
23025 };
23026
23027
23028 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23029
23030
23031      /**
23032      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23033      */
23034     
23035     owner : false,
23036     
23037      /**
23038      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23039      *                        Roo.resizable.
23040      */
23041     resizable : false,
23042      /**
23043      * @cfg {Number} height (in pixels)
23044      */   
23045     height: 300,
23046    /**
23047      * @cfg {Number} width (in pixels)
23048      */   
23049     width: 500,
23050     
23051     /**
23052      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23053      * 
23054      */
23055     stylesheets: false,
23056     
23057     // id of frame..
23058     frameId: false,
23059     
23060     // private properties
23061     validationEvent : false,
23062     deferHeight: true,
23063     initialized : false,
23064     activated : false,
23065     sourceEditMode : false,
23066     onFocus : Roo.emptyFn,
23067     iframePad:3,
23068     hideMode:'offsets',
23069     
23070     clearUp: true,
23071     
23072     // blacklist + whitelisted elements..
23073     black: false,
23074     white: false,
23075      
23076     bodyCls : '',
23077
23078     /**
23079      * Protected method that will not generally be called directly. It
23080      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23081      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23082      */
23083     getDocMarkup : function(){
23084         // body styles..
23085         var st = '';
23086         
23087         // inherit styels from page...?? 
23088         if (this.stylesheets === false) {
23089             
23090             Roo.get(document.head).select('style').each(function(node) {
23091                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23092             });
23093             
23094             Roo.get(document.head).select('link').each(function(node) { 
23095                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23096             });
23097             
23098         } else if (!this.stylesheets.length) {
23099                 // simple..
23100                 st = '<style type="text/css">' +
23101                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23102                    '</style>';
23103         } else { 
23104             st = '<style type="text/css">' +
23105                     this.stylesheets +
23106                 '</style>';
23107         }
23108         
23109         st +=  '<style type="text/css">' +
23110             'IMG { cursor: pointer } ' +
23111         '</style>';
23112
23113         var cls = 'roo-htmleditor-body';
23114         
23115         if(this.bodyCls.length){
23116             cls += ' ' + this.bodyCls;
23117         }
23118         
23119         return '<html><head>' + st  +
23120             //<style type="text/css">' +
23121             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23122             //'</style>' +
23123             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23124     },
23125
23126     // private
23127     onRender : function(ct, position)
23128     {
23129         var _t = this;
23130         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23131         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23132         
23133         
23134         this.el.dom.style.border = '0 none';
23135         this.el.dom.setAttribute('tabIndex', -1);
23136         this.el.addClass('x-hidden hide');
23137         
23138         
23139         
23140         if(Roo.isIE){ // fix IE 1px bogus margin
23141             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23142         }
23143        
23144         
23145         this.frameId = Roo.id();
23146         
23147          
23148         
23149         var iframe = this.owner.wrap.createChild({
23150             tag: 'iframe',
23151             cls: 'form-control', // bootstrap..
23152             id: this.frameId,
23153             name: this.frameId,
23154             frameBorder : 'no',
23155             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23156         }, this.el
23157         );
23158         
23159         
23160         this.iframe = iframe.dom;
23161
23162          this.assignDocWin();
23163         
23164         this.doc.designMode = 'on';
23165        
23166         this.doc.open();
23167         this.doc.write(this.getDocMarkup());
23168         this.doc.close();
23169
23170         
23171         var task = { // must defer to wait for browser to be ready
23172             run : function(){
23173                 //console.log("run task?" + this.doc.readyState);
23174                 this.assignDocWin();
23175                 if(this.doc.body || this.doc.readyState == 'complete'){
23176                     try {
23177                         this.doc.designMode="on";
23178                     } catch (e) {
23179                         return;
23180                     }
23181                     Roo.TaskMgr.stop(task);
23182                     this.initEditor.defer(10, this);
23183                 }
23184             },
23185             interval : 10,
23186             duration: 10000,
23187             scope: this
23188         };
23189         Roo.TaskMgr.start(task);
23190
23191     },
23192
23193     // private
23194     onResize : function(w, h)
23195     {
23196          Roo.log('resize: ' +w + ',' + h );
23197         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23198         if(!this.iframe){
23199             return;
23200         }
23201         if(typeof w == 'number'){
23202             
23203             this.iframe.style.width = w + 'px';
23204         }
23205         if(typeof h == 'number'){
23206             
23207             this.iframe.style.height = h + 'px';
23208             if(this.doc){
23209                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23210             }
23211         }
23212         
23213     },
23214
23215     /**
23216      * Toggles the editor between standard and source edit mode.
23217      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23218      */
23219     toggleSourceEdit : function(sourceEditMode){
23220         
23221         this.sourceEditMode = sourceEditMode === true;
23222         
23223         if(this.sourceEditMode){
23224  
23225             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23226             
23227         }else{
23228             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23229             //this.iframe.className = '';
23230             this.deferFocus();
23231         }
23232         //this.setSize(this.owner.wrap.getSize());
23233         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23234     },
23235
23236     
23237   
23238
23239     /**
23240      * Protected method that will not generally be called directly. If you need/want
23241      * custom HTML cleanup, this is the method you should override.
23242      * @param {String} html The HTML to be cleaned
23243      * return {String} The cleaned HTML
23244      */
23245     cleanHtml : function(html){
23246         html = String(html);
23247         if(html.length > 5){
23248             if(Roo.isSafari){ // strip safari nonsense
23249                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23250             }
23251         }
23252         if(html == '&nbsp;'){
23253             html = '';
23254         }
23255         return html;
23256     },
23257
23258     /**
23259      * HTML Editor -> Textarea
23260      * Protected method that will not generally be called directly. Syncs the contents
23261      * of the editor iframe with the textarea.
23262      */
23263     syncValue : function(){
23264         if(this.initialized){
23265             var bd = (this.doc.body || this.doc.documentElement);
23266             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23267             var html = bd.innerHTML;
23268             if(Roo.isSafari){
23269                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23270                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23271                 if(m && m[1]){
23272                     html = '<div style="'+m[0]+'">' + html + '</div>';
23273                 }
23274             }
23275             html = this.cleanHtml(html);
23276             // fix up the special chars.. normaly like back quotes in word...
23277             // however we do not want to do this with chinese..
23278             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23279                 
23280                 var cc = match.charCodeAt();
23281
23282                 // Get the character value, handling surrogate pairs
23283                 if (match.length == 2) {
23284                     // It's a surrogate pair, calculate the Unicode code point
23285                     var high = match.charCodeAt(0) - 0xD800;
23286                     var low  = match.charCodeAt(1) - 0xDC00;
23287                     cc = (high * 0x400) + low + 0x10000;
23288                 }  else if (
23289                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23290                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23291                     (cc >= 0xf900 && cc < 0xfb00 )
23292                 ) {
23293                         return match;
23294                 }  
23295          
23296                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23297                 return "&#" + cc + ";";
23298                 
23299                 
23300             });
23301             
23302             
23303              
23304             if(this.owner.fireEvent('beforesync', this, html) !== false){
23305                 this.el.dom.value = html;
23306                 this.owner.fireEvent('sync', this, html);
23307             }
23308         }
23309     },
23310
23311     /**
23312      * Protected method that will not generally be called directly. Pushes the value of the textarea
23313      * into the iframe editor.
23314      */
23315     pushValue : function(){
23316         if(this.initialized){
23317             var v = this.el.dom.value.trim();
23318             
23319 //            if(v.length < 1){
23320 //                v = '&#160;';
23321 //            }
23322             
23323             if(this.owner.fireEvent('beforepush', this, v) !== false){
23324                 var d = (this.doc.body || this.doc.documentElement);
23325                 d.innerHTML = v;
23326                 this.cleanUpPaste();
23327                 this.el.dom.value = d.innerHTML;
23328                 this.owner.fireEvent('push', this, v);
23329             }
23330         }
23331     },
23332
23333     // private
23334     deferFocus : function(){
23335         this.focus.defer(10, this);
23336     },
23337
23338     // doc'ed in Field
23339     focus : function(){
23340         if(this.win && !this.sourceEditMode){
23341             this.win.focus();
23342         }else{
23343             this.el.focus();
23344         }
23345     },
23346     
23347     assignDocWin: function()
23348     {
23349         var iframe = this.iframe;
23350         
23351          if(Roo.isIE){
23352             this.doc = iframe.contentWindow.document;
23353             this.win = iframe.contentWindow;
23354         } else {
23355 //            if (!Roo.get(this.frameId)) {
23356 //                return;
23357 //            }
23358 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23359 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23360             
23361             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23362                 return;
23363             }
23364             
23365             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23366             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23367         }
23368     },
23369     
23370     // private
23371     initEditor : function(){
23372         //console.log("INIT EDITOR");
23373         this.assignDocWin();
23374         
23375         
23376         
23377         this.doc.designMode="on";
23378         this.doc.open();
23379         this.doc.write(this.getDocMarkup());
23380         this.doc.close();
23381         
23382         var dbody = (this.doc.body || this.doc.documentElement);
23383         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23384         // this copies styles from the containing element into thsi one..
23385         // not sure why we need all of this..
23386         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23387         
23388         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23389         //ss['background-attachment'] = 'fixed'; // w3c
23390         dbody.bgProperties = 'fixed'; // ie
23391         //Roo.DomHelper.applyStyles(dbody, ss);
23392         Roo.EventManager.on(this.doc, {
23393             //'mousedown': this.onEditorEvent,
23394             'mouseup': this.onEditorEvent,
23395             'dblclick': this.onEditorEvent,
23396             'click': this.onEditorEvent,
23397             'keyup': this.onEditorEvent,
23398             buffer:100,
23399             scope: this
23400         });
23401         if(Roo.isGecko){
23402             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23403         }
23404         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23405             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23406         }
23407         this.initialized = true;
23408
23409         this.owner.fireEvent('initialize', this);
23410         this.pushValue();
23411     },
23412
23413     // private
23414     onDestroy : function(){
23415         
23416         
23417         
23418         if(this.rendered){
23419             
23420             //for (var i =0; i < this.toolbars.length;i++) {
23421             //    // fixme - ask toolbars for heights?
23422             //    this.toolbars[i].onDestroy();
23423            // }
23424             
23425             //this.wrap.dom.innerHTML = '';
23426             //this.wrap.remove();
23427         }
23428     },
23429
23430     // private
23431     onFirstFocus : function(){
23432         
23433         this.assignDocWin();
23434         
23435         
23436         this.activated = true;
23437          
23438     
23439         if(Roo.isGecko){ // prevent silly gecko errors
23440             this.win.focus();
23441             var s = this.win.getSelection();
23442             if(!s.focusNode || s.focusNode.nodeType != 3){
23443                 var r = s.getRangeAt(0);
23444                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23445                 r.collapse(true);
23446                 this.deferFocus();
23447             }
23448             try{
23449                 this.execCmd('useCSS', true);
23450                 this.execCmd('styleWithCSS', false);
23451             }catch(e){}
23452         }
23453         this.owner.fireEvent('activate', this);
23454     },
23455
23456     // private
23457     adjustFont: function(btn){
23458         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23459         //if(Roo.isSafari){ // safari
23460         //    adjust *= 2;
23461        // }
23462         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23463         if(Roo.isSafari){ // safari
23464             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23465             v =  (v < 10) ? 10 : v;
23466             v =  (v > 48) ? 48 : v;
23467             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23468             
23469         }
23470         
23471         
23472         v = Math.max(1, v+adjust);
23473         
23474         this.execCmd('FontSize', v  );
23475     },
23476
23477     onEditorEvent : function(e)
23478     {
23479         this.owner.fireEvent('editorevent', this, e);
23480       //  this.updateToolbar();
23481         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23482     },
23483
23484     insertTag : function(tg)
23485     {
23486         // could be a bit smarter... -> wrap the current selected tRoo..
23487         if (tg.toLowerCase() == 'span' ||
23488             tg.toLowerCase() == 'code' ||
23489             tg.toLowerCase() == 'sup' ||
23490             tg.toLowerCase() == 'sub' 
23491             ) {
23492             
23493             range = this.createRange(this.getSelection());
23494             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23495             wrappingNode.appendChild(range.extractContents());
23496             range.insertNode(wrappingNode);
23497
23498             return;
23499             
23500             
23501             
23502         }
23503         this.execCmd("formatblock",   tg);
23504         
23505     },
23506     
23507     insertText : function(txt)
23508     {
23509         
23510         
23511         var range = this.createRange();
23512         range.deleteContents();
23513                //alert(Sender.getAttribute('label'));
23514                
23515         range.insertNode(this.doc.createTextNode(txt));
23516     } ,
23517     
23518      
23519
23520     /**
23521      * Executes a Midas editor command on the editor document and performs necessary focus and
23522      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23523      * @param {String} cmd The Midas command
23524      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23525      */
23526     relayCmd : function(cmd, value){
23527         this.win.focus();
23528         this.execCmd(cmd, value);
23529         this.owner.fireEvent('editorevent', this);
23530         //this.updateToolbar();
23531         this.owner.deferFocus();
23532     },
23533
23534     /**
23535      * Executes a Midas editor command directly on the editor document.
23536      * For visual commands, you should use {@link #relayCmd} instead.
23537      * <b>This should only be called after the editor is initialized.</b>
23538      * @param {String} cmd The Midas command
23539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23540      */
23541     execCmd : function(cmd, value){
23542         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23543         this.syncValue();
23544     },
23545  
23546  
23547    
23548     /**
23549      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23550      * to insert tRoo.
23551      * @param {String} text | dom node.. 
23552      */
23553     insertAtCursor : function(text)
23554     {
23555         
23556         if(!this.activated){
23557             return;
23558         }
23559         /*
23560         if(Roo.isIE){
23561             this.win.focus();
23562             var r = this.doc.selection.createRange();
23563             if(r){
23564                 r.collapse(true);
23565                 r.pasteHTML(text);
23566                 this.syncValue();
23567                 this.deferFocus();
23568             
23569             }
23570             return;
23571         }
23572         */
23573         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23574             this.win.focus();
23575             
23576             
23577             // from jquery ui (MIT licenced)
23578             var range, node;
23579             var win = this.win;
23580             
23581             if (win.getSelection && win.getSelection().getRangeAt) {
23582                 range = win.getSelection().getRangeAt(0);
23583                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23584                 range.insertNode(node);
23585             } else if (win.document.selection && win.document.selection.createRange) {
23586                 // no firefox support
23587                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23588                 win.document.selection.createRange().pasteHTML(txt);
23589             } else {
23590                 // no firefox support
23591                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23592                 this.execCmd('InsertHTML', txt);
23593             } 
23594             
23595             this.syncValue();
23596             
23597             this.deferFocus();
23598         }
23599     },
23600  // private
23601     mozKeyPress : function(e){
23602         if(e.ctrlKey){
23603             var c = e.getCharCode(), cmd;
23604           
23605             if(c > 0){
23606                 c = String.fromCharCode(c).toLowerCase();
23607                 switch(c){
23608                     case 'b':
23609                         cmd = 'bold';
23610                         break;
23611                     case 'i':
23612                         cmd = 'italic';
23613                         break;
23614                     
23615                     case 'u':
23616                         cmd = 'underline';
23617                         break;
23618                     
23619                     case 'v':
23620                         this.cleanUpPaste.defer(100, this);
23621                         return;
23622                         
23623                 }
23624                 if(cmd){
23625                     this.win.focus();
23626                     this.execCmd(cmd);
23627                     this.deferFocus();
23628                     e.preventDefault();
23629                 }
23630                 
23631             }
23632         }
23633     },
23634
23635     // private
23636     fixKeys : function(){ // load time branching for fastest keydown performance
23637         if(Roo.isIE){
23638             return function(e){
23639                 var k = e.getKey(), r;
23640                 if(k == e.TAB){
23641                     e.stopEvent();
23642                     r = this.doc.selection.createRange();
23643                     if(r){
23644                         r.collapse(true);
23645                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23646                         this.deferFocus();
23647                     }
23648                     return;
23649                 }
23650                 
23651                 if(k == e.ENTER){
23652                     r = this.doc.selection.createRange();
23653                     if(r){
23654                         var target = r.parentElement();
23655                         if(!target || target.tagName.toLowerCase() != 'li'){
23656                             e.stopEvent();
23657                             r.pasteHTML('<br />');
23658                             r.collapse(false);
23659                             r.select();
23660                         }
23661                     }
23662                 }
23663                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23664                     this.cleanUpPaste.defer(100, this);
23665                     return;
23666                 }
23667                 
23668                 
23669             };
23670         }else if(Roo.isOpera){
23671             return function(e){
23672                 var k = e.getKey();
23673                 if(k == e.TAB){
23674                     e.stopEvent();
23675                     this.win.focus();
23676                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23677                     this.deferFocus();
23678                 }
23679                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23680                     this.cleanUpPaste.defer(100, this);
23681                     return;
23682                 }
23683                 
23684             };
23685         }else if(Roo.isSafari){
23686             return function(e){
23687                 var k = e.getKey();
23688                 
23689                 if(k == e.TAB){
23690                     e.stopEvent();
23691                     this.execCmd('InsertText','\t');
23692                     this.deferFocus();
23693                     return;
23694                 }
23695                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23696                     this.cleanUpPaste.defer(100, this);
23697                     return;
23698                 }
23699                 
23700              };
23701         }
23702     }(),
23703     
23704     getAllAncestors: function()
23705     {
23706         var p = this.getSelectedNode();
23707         var a = [];
23708         if (!p) {
23709             a.push(p); // push blank onto stack..
23710             p = this.getParentElement();
23711         }
23712         
23713         
23714         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23715             a.push(p);
23716             p = p.parentNode;
23717         }
23718         a.push(this.doc.body);
23719         return a;
23720     },
23721     lastSel : false,
23722     lastSelNode : false,
23723     
23724     
23725     getSelection : function() 
23726     {
23727         this.assignDocWin();
23728         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23729     },
23730     
23731     getSelectedNode: function() 
23732     {
23733         // this may only work on Gecko!!!
23734         
23735         // should we cache this!!!!
23736         
23737         
23738         
23739          
23740         var range = this.createRange(this.getSelection()).cloneRange();
23741         
23742         if (Roo.isIE) {
23743             var parent = range.parentElement();
23744             while (true) {
23745                 var testRange = range.duplicate();
23746                 testRange.moveToElementText(parent);
23747                 if (testRange.inRange(range)) {
23748                     break;
23749                 }
23750                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23751                     break;
23752                 }
23753                 parent = parent.parentElement;
23754             }
23755             return parent;
23756         }
23757         
23758         // is ancestor a text element.
23759         var ac =  range.commonAncestorContainer;
23760         if (ac.nodeType == 3) {
23761             ac = ac.parentNode;
23762         }
23763         
23764         var ar = ac.childNodes;
23765          
23766         var nodes = [];
23767         var other_nodes = [];
23768         var has_other_nodes = false;
23769         for (var i=0;i<ar.length;i++) {
23770             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23771                 continue;
23772             }
23773             // fullly contained node.
23774             
23775             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23776                 nodes.push(ar[i]);
23777                 continue;
23778             }
23779             
23780             // probably selected..
23781             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23782                 other_nodes.push(ar[i]);
23783                 continue;
23784             }
23785             // outer..
23786             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23787                 continue;
23788             }
23789             
23790             
23791             has_other_nodes = true;
23792         }
23793         if (!nodes.length && other_nodes.length) {
23794             nodes= other_nodes;
23795         }
23796         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23797             return false;
23798         }
23799         
23800         return nodes[0];
23801     },
23802     createRange: function(sel)
23803     {
23804         // this has strange effects when using with 
23805         // top toolbar - not sure if it's a great idea.
23806         //this.editor.contentWindow.focus();
23807         if (typeof sel != "undefined") {
23808             try {
23809                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23810             } catch(e) {
23811                 return this.doc.createRange();
23812             }
23813         } else {
23814             return this.doc.createRange();
23815         }
23816     },
23817     getParentElement: function()
23818     {
23819         
23820         this.assignDocWin();
23821         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23822         
23823         var range = this.createRange(sel);
23824          
23825         try {
23826             var p = range.commonAncestorContainer;
23827             while (p.nodeType == 3) { // text node
23828                 p = p.parentNode;
23829             }
23830             return p;
23831         } catch (e) {
23832             return null;
23833         }
23834     
23835     },
23836     /***
23837      *
23838      * Range intersection.. the hard stuff...
23839      *  '-1' = before
23840      *  '0' = hits..
23841      *  '1' = after.
23842      *         [ -- selected range --- ]
23843      *   [fail]                        [fail]
23844      *
23845      *    basically..
23846      *      if end is before start or  hits it. fail.
23847      *      if start is after end or hits it fail.
23848      *
23849      *   if either hits (but other is outside. - then it's not 
23850      *   
23851      *    
23852      **/
23853     
23854     
23855     // @see http://www.thismuchiknow.co.uk/?p=64.
23856     rangeIntersectsNode : function(range, node)
23857     {
23858         var nodeRange = node.ownerDocument.createRange();
23859         try {
23860             nodeRange.selectNode(node);
23861         } catch (e) {
23862             nodeRange.selectNodeContents(node);
23863         }
23864     
23865         var rangeStartRange = range.cloneRange();
23866         rangeStartRange.collapse(true);
23867     
23868         var rangeEndRange = range.cloneRange();
23869         rangeEndRange.collapse(false);
23870     
23871         var nodeStartRange = nodeRange.cloneRange();
23872         nodeStartRange.collapse(true);
23873     
23874         var nodeEndRange = nodeRange.cloneRange();
23875         nodeEndRange.collapse(false);
23876     
23877         return rangeStartRange.compareBoundaryPoints(
23878                  Range.START_TO_START, nodeEndRange) == -1 &&
23879                rangeEndRange.compareBoundaryPoints(
23880                  Range.START_TO_START, nodeStartRange) == 1;
23881         
23882          
23883     },
23884     rangeCompareNode : function(range, node)
23885     {
23886         var nodeRange = node.ownerDocument.createRange();
23887         try {
23888             nodeRange.selectNode(node);
23889         } catch (e) {
23890             nodeRange.selectNodeContents(node);
23891         }
23892         
23893         
23894         range.collapse(true);
23895     
23896         nodeRange.collapse(true);
23897      
23898         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23899         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23900          
23901         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23902         
23903         var nodeIsBefore   =  ss == 1;
23904         var nodeIsAfter    = ee == -1;
23905         
23906         if (nodeIsBefore && nodeIsAfter) {
23907             return 0; // outer
23908         }
23909         if (!nodeIsBefore && nodeIsAfter) {
23910             return 1; //right trailed.
23911         }
23912         
23913         if (nodeIsBefore && !nodeIsAfter) {
23914             return 2;  // left trailed.
23915         }
23916         // fully contined.
23917         return 3;
23918     },
23919
23920     // private? - in a new class?
23921     cleanUpPaste :  function()
23922     {
23923         // cleans up the whole document..
23924         Roo.log('cleanuppaste');
23925         
23926         this.cleanUpChildren(this.doc.body);
23927         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23928         if (clean != this.doc.body.innerHTML) {
23929             this.doc.body.innerHTML = clean;
23930         }
23931         
23932     },
23933     
23934     cleanWordChars : function(input) {// change the chars to hex code
23935         var he = Roo.HtmlEditorCore;
23936         
23937         var output = input;
23938         Roo.each(he.swapCodes, function(sw) { 
23939             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23940             
23941             output = output.replace(swapper, sw[1]);
23942         });
23943         
23944         return output;
23945     },
23946     
23947     
23948     cleanUpChildren : function (n)
23949     {
23950         if (!n.childNodes.length) {
23951             return;
23952         }
23953         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23954            this.cleanUpChild(n.childNodes[i]);
23955         }
23956     },
23957     
23958     
23959         
23960     
23961     cleanUpChild : function (node)
23962     {
23963         var ed = this;
23964         //console.log(node);
23965         if (node.nodeName == "#text") {
23966             // clean up silly Windows -- stuff?
23967             return; 
23968         }
23969         if (node.nodeName == "#comment") {
23970             node.parentNode.removeChild(node);
23971             // clean up silly Windows -- stuff?
23972             return; 
23973         }
23974         var lcname = node.tagName.toLowerCase();
23975         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23976         // whitelist of tags..
23977         
23978         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23979             // remove node.
23980             node.parentNode.removeChild(node);
23981             return;
23982             
23983         }
23984         
23985         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23986         
23987         // spans with no attributes - just remove them..
23988         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23989             remove_keep_children = true;
23990         }
23991         
23992         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23993         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23994         
23995         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23996         //    remove_keep_children = true;
23997         //}
23998         
23999         if (remove_keep_children) {
24000             this.cleanUpChildren(node);
24001             // inserts everything just before this node...
24002             while (node.childNodes.length) {
24003                 var cn = node.childNodes[0];
24004                 node.removeChild(cn);
24005                 node.parentNode.insertBefore(cn, node);
24006             }
24007             node.parentNode.removeChild(node);
24008             return;
24009         }
24010         
24011         if (!node.attributes || !node.attributes.length) {
24012             
24013           
24014             
24015             
24016             this.cleanUpChildren(node);
24017             return;
24018         }
24019         
24020         function cleanAttr(n,v)
24021         {
24022             
24023             if (v.match(/^\./) || v.match(/^\//)) {
24024                 return;
24025             }
24026             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24027                 return;
24028             }
24029             if (v.match(/^#/)) {
24030                 return;
24031             }
24032 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24033             node.removeAttribute(n);
24034             
24035         }
24036         
24037         var cwhite = this.cwhite;
24038         var cblack = this.cblack;
24039             
24040         function cleanStyle(n,v)
24041         {
24042             if (v.match(/expression/)) { //XSS?? should we even bother..
24043                 node.removeAttribute(n);
24044                 return;
24045             }
24046             
24047             var parts = v.split(/;/);
24048             var clean = [];
24049             
24050             Roo.each(parts, function(p) {
24051                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24052                 if (!p.length) {
24053                     return true;
24054                 }
24055                 var l = p.split(':').shift().replace(/\s+/g,'');
24056                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24057                 
24058                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24059 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24060                     //node.removeAttribute(n);
24061                     return true;
24062                 }
24063                 //Roo.log()
24064                 // only allow 'c whitelisted system attributes'
24065                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24066 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24067                     //node.removeAttribute(n);
24068                     return true;
24069                 }
24070                 
24071                 
24072                  
24073                 
24074                 clean.push(p);
24075                 return true;
24076             });
24077             if (clean.length) { 
24078                 node.setAttribute(n, clean.join(';'));
24079             } else {
24080                 node.removeAttribute(n);
24081             }
24082             
24083         }
24084         
24085         
24086         for (var i = node.attributes.length-1; i > -1 ; i--) {
24087             var a = node.attributes[i];
24088             //console.log(a);
24089             
24090             if (a.name.toLowerCase().substr(0,2)=='on')  {
24091                 node.removeAttribute(a.name);
24092                 continue;
24093             }
24094             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24095                 node.removeAttribute(a.name);
24096                 continue;
24097             }
24098             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24099                 cleanAttr(a.name,a.value); // fixme..
24100                 continue;
24101             }
24102             if (a.name == 'style') {
24103                 cleanStyle(a.name,a.value);
24104                 continue;
24105             }
24106             /// clean up MS crap..
24107             // tecnically this should be a list of valid class'es..
24108             
24109             
24110             if (a.name == 'class') {
24111                 if (a.value.match(/^Mso/)) {
24112                     node.removeAttribute('class');
24113                 }
24114                 
24115                 if (a.value.match(/^body$/)) {
24116                     node.removeAttribute('class');
24117                 }
24118                 continue;
24119             }
24120             
24121             // style cleanup!?
24122             // class cleanup?
24123             
24124         }
24125         
24126         
24127         this.cleanUpChildren(node);
24128         
24129         
24130     },
24131     
24132     /**
24133      * Clean up MS wordisms...
24134      */
24135     cleanWord : function(node)
24136     {
24137         if (!node) {
24138             this.cleanWord(this.doc.body);
24139             return;
24140         }
24141         
24142         if(
24143                 node.nodeName == 'SPAN' &&
24144                 !node.hasAttributes() &&
24145                 node.childNodes.length == 1 &&
24146                 node.firstChild.nodeName == "#text"  
24147         ) {
24148             var textNode = node.firstChild;
24149             node.removeChild(textNode);
24150             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24151                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24152             }
24153             node.parentNode.insertBefore(textNode, node);
24154             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24155                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24156             }
24157             node.parentNode.removeChild(node);
24158         }
24159         
24160         if (node.nodeName == "#text") {
24161             // clean up silly Windows -- stuff?
24162             return; 
24163         }
24164         if (node.nodeName == "#comment") {
24165             node.parentNode.removeChild(node);
24166             // clean up silly Windows -- stuff?
24167             return; 
24168         }
24169         
24170         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24171             node.parentNode.removeChild(node);
24172             return;
24173         }
24174         //Roo.log(node.tagName);
24175         // remove - but keep children..
24176         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24177             //Roo.log('-- removed');
24178             while (node.childNodes.length) {
24179                 var cn = node.childNodes[0];
24180                 node.removeChild(cn);
24181                 node.parentNode.insertBefore(cn, node);
24182                 // move node to parent - and clean it..
24183                 this.cleanWord(cn);
24184             }
24185             node.parentNode.removeChild(node);
24186             /// no need to iterate chidlren = it's got none..
24187             //this.iterateChildren(node, this.cleanWord);
24188             return;
24189         }
24190         // clean styles
24191         if (node.className.length) {
24192             
24193             var cn = node.className.split(/\W+/);
24194             var cna = [];
24195             Roo.each(cn, function(cls) {
24196                 if (cls.match(/Mso[a-zA-Z]+/)) {
24197                     return;
24198                 }
24199                 cna.push(cls);
24200             });
24201             node.className = cna.length ? cna.join(' ') : '';
24202             if (!cna.length) {
24203                 node.removeAttribute("class");
24204             }
24205         }
24206         
24207         if (node.hasAttribute("lang")) {
24208             node.removeAttribute("lang");
24209         }
24210         
24211         if (node.hasAttribute("style")) {
24212             
24213             var styles = node.getAttribute("style").split(";");
24214             var nstyle = [];
24215             Roo.each(styles, function(s) {
24216                 if (!s.match(/:/)) {
24217                     return;
24218                 }
24219                 var kv = s.split(":");
24220                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24221                     return;
24222                 }
24223                 // what ever is left... we allow.
24224                 nstyle.push(s);
24225             });
24226             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24227             if (!nstyle.length) {
24228                 node.removeAttribute('style');
24229             }
24230         }
24231         this.iterateChildren(node, this.cleanWord);
24232         
24233         
24234         
24235     },
24236     /**
24237      * iterateChildren of a Node, calling fn each time, using this as the scole..
24238      * @param {DomNode} node node to iterate children of.
24239      * @param {Function} fn method of this class to call on each item.
24240      */
24241     iterateChildren : function(node, fn)
24242     {
24243         if (!node.childNodes.length) {
24244                 return;
24245         }
24246         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24247            fn.call(this, node.childNodes[i])
24248         }
24249     },
24250     
24251     
24252     /**
24253      * cleanTableWidths.
24254      *
24255      * Quite often pasting from word etc.. results in tables with column and widths.
24256      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24257      *
24258      */
24259     cleanTableWidths : function(node)
24260     {
24261          
24262          
24263         if (!node) {
24264             this.cleanTableWidths(this.doc.body);
24265             return;
24266         }
24267         
24268         // ignore list...
24269         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24270             return; 
24271         }
24272         Roo.log(node.tagName);
24273         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24274             this.iterateChildren(node, this.cleanTableWidths);
24275             return;
24276         }
24277         if (node.hasAttribute('width')) {
24278             node.removeAttribute('width');
24279         }
24280         
24281          
24282         if (node.hasAttribute("style")) {
24283             // pretty basic...
24284             
24285             var styles = node.getAttribute("style").split(";");
24286             var nstyle = [];
24287             Roo.each(styles, function(s) {
24288                 if (!s.match(/:/)) {
24289                     return;
24290                 }
24291                 var kv = s.split(":");
24292                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24293                     return;
24294                 }
24295                 // what ever is left... we allow.
24296                 nstyle.push(s);
24297             });
24298             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24299             if (!nstyle.length) {
24300                 node.removeAttribute('style');
24301             }
24302         }
24303         
24304         this.iterateChildren(node, this.cleanTableWidths);
24305         
24306         
24307     },
24308     
24309     
24310     
24311     
24312     domToHTML : function(currentElement, depth, nopadtext) {
24313         
24314         depth = depth || 0;
24315         nopadtext = nopadtext || false;
24316     
24317         if (!currentElement) {
24318             return this.domToHTML(this.doc.body);
24319         }
24320         
24321         //Roo.log(currentElement);
24322         var j;
24323         var allText = false;
24324         var nodeName = currentElement.nodeName;
24325         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24326         
24327         if  (nodeName == '#text') {
24328             
24329             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24330         }
24331         
24332         
24333         var ret = '';
24334         if (nodeName != 'BODY') {
24335              
24336             var i = 0;
24337             // Prints the node tagName, such as <A>, <IMG>, etc
24338             if (tagName) {
24339                 var attr = [];
24340                 for(i = 0; i < currentElement.attributes.length;i++) {
24341                     // quoting?
24342                     var aname = currentElement.attributes.item(i).name;
24343                     if (!currentElement.attributes.item(i).value.length) {
24344                         continue;
24345                     }
24346                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24347                 }
24348                 
24349                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24350             } 
24351             else {
24352                 
24353                 // eack
24354             }
24355         } else {
24356             tagName = false;
24357         }
24358         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24359             return ret;
24360         }
24361         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24362             nopadtext = true;
24363         }
24364         
24365         
24366         // Traverse the tree
24367         i = 0;
24368         var currentElementChild = currentElement.childNodes.item(i);
24369         var allText = true;
24370         var innerHTML  = '';
24371         lastnode = '';
24372         while (currentElementChild) {
24373             // Formatting code (indent the tree so it looks nice on the screen)
24374             var nopad = nopadtext;
24375             if (lastnode == 'SPAN') {
24376                 nopad  = true;
24377             }
24378             // text
24379             if  (currentElementChild.nodeName == '#text') {
24380                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24381                 toadd = nopadtext ? toadd : toadd.trim();
24382                 if (!nopad && toadd.length > 80) {
24383                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24384                 }
24385                 innerHTML  += toadd;
24386                 
24387                 i++;
24388                 currentElementChild = currentElement.childNodes.item(i);
24389                 lastNode = '';
24390                 continue;
24391             }
24392             allText = false;
24393             
24394             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24395                 
24396             // Recursively traverse the tree structure of the child node
24397             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24398             lastnode = currentElementChild.nodeName;
24399             i++;
24400             currentElementChild=currentElement.childNodes.item(i);
24401         }
24402         
24403         ret += innerHTML;
24404         
24405         if (!allText) {
24406                 // The remaining code is mostly for formatting the tree
24407             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24408         }
24409         
24410         
24411         if (tagName) {
24412             ret+= "</"+tagName+">";
24413         }
24414         return ret;
24415         
24416     },
24417         
24418     applyBlacklists : function()
24419     {
24420         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24421         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24422         
24423         this.white = [];
24424         this.black = [];
24425         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24426             if (b.indexOf(tag) > -1) {
24427                 return;
24428             }
24429             this.white.push(tag);
24430             
24431         }, this);
24432         
24433         Roo.each(w, function(tag) {
24434             if (b.indexOf(tag) > -1) {
24435                 return;
24436             }
24437             if (this.white.indexOf(tag) > -1) {
24438                 return;
24439             }
24440             this.white.push(tag);
24441             
24442         }, this);
24443         
24444         
24445         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24446             if (w.indexOf(tag) > -1) {
24447                 return;
24448             }
24449             this.black.push(tag);
24450             
24451         }, this);
24452         
24453         Roo.each(b, function(tag) {
24454             if (w.indexOf(tag) > -1) {
24455                 return;
24456             }
24457             if (this.black.indexOf(tag) > -1) {
24458                 return;
24459             }
24460             this.black.push(tag);
24461             
24462         }, this);
24463         
24464         
24465         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24466         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24467         
24468         this.cwhite = [];
24469         this.cblack = [];
24470         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24471             if (b.indexOf(tag) > -1) {
24472                 return;
24473             }
24474             this.cwhite.push(tag);
24475             
24476         }, this);
24477         
24478         Roo.each(w, function(tag) {
24479             if (b.indexOf(tag) > -1) {
24480                 return;
24481             }
24482             if (this.cwhite.indexOf(tag) > -1) {
24483                 return;
24484             }
24485             this.cwhite.push(tag);
24486             
24487         }, this);
24488         
24489         
24490         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24491             if (w.indexOf(tag) > -1) {
24492                 return;
24493             }
24494             this.cblack.push(tag);
24495             
24496         }, this);
24497         
24498         Roo.each(b, function(tag) {
24499             if (w.indexOf(tag) > -1) {
24500                 return;
24501             }
24502             if (this.cblack.indexOf(tag) > -1) {
24503                 return;
24504             }
24505             this.cblack.push(tag);
24506             
24507         }, this);
24508     },
24509     
24510     setStylesheets : function(stylesheets)
24511     {
24512         if(typeof(stylesheets) == 'string'){
24513             Roo.get(this.iframe.contentDocument.head).createChild({
24514                 tag : 'link',
24515                 rel : 'stylesheet',
24516                 type : 'text/css',
24517                 href : stylesheets
24518             });
24519             
24520             return;
24521         }
24522         var _this = this;
24523      
24524         Roo.each(stylesheets, function(s) {
24525             if(!s.length){
24526                 return;
24527             }
24528             
24529             Roo.get(_this.iframe.contentDocument.head).createChild({
24530                 tag : 'link',
24531                 rel : 'stylesheet',
24532                 type : 'text/css',
24533                 href : s
24534             });
24535         });
24536
24537         
24538     },
24539     
24540     removeStylesheets : function()
24541     {
24542         var _this = this;
24543         
24544         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24545             s.remove();
24546         });
24547     },
24548     
24549     setStyle : function(style)
24550     {
24551         Roo.get(this.iframe.contentDocument.head).createChild({
24552             tag : 'style',
24553             type : 'text/css',
24554             html : style
24555         });
24556
24557         return;
24558     }
24559     
24560     // hide stuff that is not compatible
24561     /**
24562      * @event blur
24563      * @hide
24564      */
24565     /**
24566      * @event change
24567      * @hide
24568      */
24569     /**
24570      * @event focus
24571      * @hide
24572      */
24573     /**
24574      * @event specialkey
24575      * @hide
24576      */
24577     /**
24578      * @cfg {String} fieldClass @hide
24579      */
24580     /**
24581      * @cfg {String} focusClass @hide
24582      */
24583     /**
24584      * @cfg {String} autoCreate @hide
24585      */
24586     /**
24587      * @cfg {String} inputType @hide
24588      */
24589     /**
24590      * @cfg {String} invalidClass @hide
24591      */
24592     /**
24593      * @cfg {String} invalidText @hide
24594      */
24595     /**
24596      * @cfg {String} msgFx @hide
24597      */
24598     /**
24599      * @cfg {String} validateOnBlur @hide
24600      */
24601 });
24602
24603 Roo.HtmlEditorCore.white = [
24604         'area', 'br', 'img', 'input', 'hr', 'wbr',
24605         
24606        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24607        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24608        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24609        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24610        'table',   'ul',         'xmp', 
24611        
24612        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24613       'thead',   'tr', 
24614      
24615       'dir', 'menu', 'ol', 'ul', 'dl',
24616        
24617       'embed',  'object'
24618 ];
24619
24620
24621 Roo.HtmlEditorCore.black = [
24622     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24623         'applet', // 
24624         'base',   'basefont', 'bgsound', 'blink',  'body', 
24625         'frame',  'frameset', 'head',    'html',   'ilayer', 
24626         'iframe', 'layer',  'link',     'meta',    'object',   
24627         'script', 'style' ,'title',  'xml' // clean later..
24628 ];
24629 Roo.HtmlEditorCore.clean = [
24630     'script', 'style', 'title', 'xml'
24631 ];
24632 Roo.HtmlEditorCore.remove = [
24633     'font'
24634 ];
24635 // attributes..
24636
24637 Roo.HtmlEditorCore.ablack = [
24638     'on'
24639 ];
24640     
24641 Roo.HtmlEditorCore.aclean = [ 
24642     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24643 ];
24644
24645 // protocols..
24646 Roo.HtmlEditorCore.pwhite= [
24647         'http',  'https',  'mailto'
24648 ];
24649
24650 // white listed style attributes.
24651 Roo.HtmlEditorCore.cwhite= [
24652       //  'text-align', /// default is to allow most things..
24653       
24654          
24655 //        'font-size'//??
24656 ];
24657
24658 // black listed style attributes.
24659 Roo.HtmlEditorCore.cblack= [
24660       //  'font-size' -- this can be set by the project 
24661 ];
24662
24663
24664 Roo.HtmlEditorCore.swapCodes   =[ 
24665     [    8211, "--" ], 
24666     [    8212, "--" ], 
24667     [    8216,  "'" ],  
24668     [    8217, "'" ],  
24669     [    8220, '"' ],  
24670     [    8221, '"' ],  
24671     [    8226, "*" ],  
24672     [    8230, "..." ]
24673 ]; 
24674
24675     /*
24676  * - LGPL
24677  *
24678  * HtmlEditor
24679  * 
24680  */
24681
24682 /**
24683  * @class Roo.bootstrap.HtmlEditor
24684  * @extends Roo.bootstrap.TextArea
24685  * Bootstrap HtmlEditor class
24686
24687  * @constructor
24688  * Create a new HtmlEditor
24689  * @param {Object} config The config object
24690  */
24691
24692 Roo.bootstrap.HtmlEditor = function(config){
24693     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24694     if (!this.toolbars) {
24695         this.toolbars = [];
24696     }
24697     
24698     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24699     this.addEvents({
24700             /**
24701              * @event initialize
24702              * Fires when the editor is fully initialized (including the iframe)
24703              * @param {HtmlEditor} this
24704              */
24705             initialize: true,
24706             /**
24707              * @event activate
24708              * Fires when the editor is first receives the focus. Any insertion must wait
24709              * until after this event.
24710              * @param {HtmlEditor} this
24711              */
24712             activate: true,
24713              /**
24714              * @event beforesync
24715              * Fires before the textarea is updated with content from the editor iframe. Return false
24716              * to cancel the sync.
24717              * @param {HtmlEditor} this
24718              * @param {String} html
24719              */
24720             beforesync: true,
24721              /**
24722              * @event beforepush
24723              * Fires before the iframe editor is updated with content from the textarea. Return false
24724              * to cancel the push.
24725              * @param {HtmlEditor} this
24726              * @param {String} html
24727              */
24728             beforepush: true,
24729              /**
24730              * @event sync
24731              * Fires when the textarea is updated with content from the editor iframe.
24732              * @param {HtmlEditor} this
24733              * @param {String} html
24734              */
24735             sync: true,
24736              /**
24737              * @event push
24738              * Fires when the iframe editor is updated with content from the textarea.
24739              * @param {HtmlEditor} this
24740              * @param {String} html
24741              */
24742             push: true,
24743              /**
24744              * @event editmodechange
24745              * Fires when the editor switches edit modes
24746              * @param {HtmlEditor} this
24747              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24748              */
24749             editmodechange: true,
24750             /**
24751              * @event editorevent
24752              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24753              * @param {HtmlEditor} this
24754              */
24755             editorevent: true,
24756             /**
24757              * @event firstfocus
24758              * Fires when on first focus - needed by toolbars..
24759              * @param {HtmlEditor} this
24760              */
24761             firstfocus: true,
24762             /**
24763              * @event autosave
24764              * Auto save the htmlEditor value as a file into Events
24765              * @param {HtmlEditor} this
24766              */
24767             autosave: true,
24768             /**
24769              * @event savedpreview
24770              * preview the saved version of htmlEditor
24771              * @param {HtmlEditor} this
24772              */
24773             savedpreview: true
24774         });
24775 };
24776
24777
24778 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24779     
24780     
24781       /**
24782      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24783      */
24784     toolbars : false,
24785     
24786      /**
24787     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24788     */
24789     btns : [],
24790    
24791      /**
24792      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24793      *                        Roo.resizable.
24794      */
24795     resizable : false,
24796      /**
24797      * @cfg {Number} height (in pixels)
24798      */   
24799     height: 300,
24800    /**
24801      * @cfg {Number} width (in pixels)
24802      */   
24803     width: false,
24804     
24805     /**
24806      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24807      * 
24808      */
24809     stylesheets: false,
24810     
24811     // id of frame..
24812     frameId: false,
24813     
24814     // private properties
24815     validationEvent : false,
24816     deferHeight: true,
24817     initialized : false,
24818     activated : false,
24819     
24820     onFocus : Roo.emptyFn,
24821     iframePad:3,
24822     hideMode:'offsets',
24823     
24824     tbContainer : false,
24825     
24826     bodyCls : '',
24827     
24828     toolbarContainer :function() {
24829         return this.wrap.select('.x-html-editor-tb',true).first();
24830     },
24831
24832     /**
24833      * Protected method that will not generally be called directly. It
24834      * is called when the editor creates its toolbar. Override this method if you need to
24835      * add custom toolbar buttons.
24836      * @param {HtmlEditor} editor
24837      */
24838     createToolbar : function(){
24839         Roo.log('renewing');
24840         Roo.log("create toolbars");
24841         
24842         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24843         this.toolbars[0].render(this.toolbarContainer());
24844         
24845         return;
24846         
24847 //        if (!editor.toolbars || !editor.toolbars.length) {
24848 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24849 //        }
24850 //        
24851 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24852 //            editor.toolbars[i] = Roo.factory(
24853 //                    typeof(editor.toolbars[i]) == 'string' ?
24854 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24855 //                Roo.bootstrap.HtmlEditor);
24856 //            editor.toolbars[i].init(editor);
24857 //        }
24858     },
24859
24860      
24861     // private
24862     onRender : function(ct, position)
24863     {
24864        // Roo.log("Call onRender: " + this.xtype);
24865         var _t = this;
24866         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24867       
24868         this.wrap = this.inputEl().wrap({
24869             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24870         });
24871         
24872         this.editorcore.onRender(ct, position);
24873          
24874         if (this.resizable) {
24875             this.resizeEl = new Roo.Resizable(this.wrap, {
24876                 pinned : true,
24877                 wrap: true,
24878                 dynamic : true,
24879                 minHeight : this.height,
24880                 height: this.height,
24881                 handles : this.resizable,
24882                 width: this.width,
24883                 listeners : {
24884                     resize : function(r, w, h) {
24885                         _t.onResize(w,h); // -something
24886                     }
24887                 }
24888             });
24889             
24890         }
24891         this.createToolbar(this);
24892        
24893         
24894         if(!this.width && this.resizable){
24895             this.setSize(this.wrap.getSize());
24896         }
24897         if (this.resizeEl) {
24898             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24899             // should trigger onReize..
24900         }
24901         
24902     },
24903
24904     // private
24905     onResize : function(w, h)
24906     {
24907         Roo.log('resize: ' +w + ',' + h );
24908         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24909         var ew = false;
24910         var eh = false;
24911         
24912         if(this.inputEl() ){
24913             if(typeof w == 'number'){
24914                 var aw = w - this.wrap.getFrameWidth('lr');
24915                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24916                 ew = aw;
24917             }
24918             if(typeof h == 'number'){
24919                  var tbh = -11;  // fixme it needs to tool bar size!
24920                 for (var i =0; i < this.toolbars.length;i++) {
24921                     // fixme - ask toolbars for heights?
24922                     tbh += this.toolbars[i].el.getHeight();
24923                     //if (this.toolbars[i].footer) {
24924                     //    tbh += this.toolbars[i].footer.el.getHeight();
24925                     //}
24926                 }
24927               
24928                 
24929                 
24930                 
24931                 
24932                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24933                 ah -= 5; // knock a few pixes off for look..
24934                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24935                 var eh = ah;
24936             }
24937         }
24938         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24939         this.editorcore.onResize(ew,eh);
24940         
24941     },
24942
24943     /**
24944      * Toggles the editor between standard and source edit mode.
24945      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24946      */
24947     toggleSourceEdit : function(sourceEditMode)
24948     {
24949         this.editorcore.toggleSourceEdit(sourceEditMode);
24950         
24951         if(this.editorcore.sourceEditMode){
24952             Roo.log('editor - showing textarea');
24953             
24954 //            Roo.log('in');
24955 //            Roo.log(this.syncValue());
24956             this.syncValue();
24957             this.inputEl().removeClass(['hide', 'x-hidden']);
24958             this.inputEl().dom.removeAttribute('tabIndex');
24959             this.inputEl().focus();
24960         }else{
24961             Roo.log('editor - hiding textarea');
24962 //            Roo.log('out')
24963 //            Roo.log(this.pushValue()); 
24964             this.pushValue();
24965             
24966             this.inputEl().addClass(['hide', 'x-hidden']);
24967             this.inputEl().dom.setAttribute('tabIndex', -1);
24968             //this.deferFocus();
24969         }
24970          
24971         if(this.resizable){
24972             this.setSize(this.wrap.getSize());
24973         }
24974         
24975         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24976     },
24977  
24978     // private (for BoxComponent)
24979     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24980
24981     // private (for BoxComponent)
24982     getResizeEl : function(){
24983         return this.wrap;
24984     },
24985
24986     // private (for BoxComponent)
24987     getPositionEl : function(){
24988         return this.wrap;
24989     },
24990
24991     // private
24992     initEvents : function(){
24993         this.originalValue = this.getValue();
24994     },
24995
24996 //    /**
24997 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24998 //     * @method
24999 //     */
25000 //    markInvalid : Roo.emptyFn,
25001 //    /**
25002 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25003 //     * @method
25004 //     */
25005 //    clearInvalid : Roo.emptyFn,
25006
25007     setValue : function(v){
25008         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25009         this.editorcore.pushValue();
25010     },
25011
25012      
25013     // private
25014     deferFocus : function(){
25015         this.focus.defer(10, this);
25016     },
25017
25018     // doc'ed in Field
25019     focus : function(){
25020         this.editorcore.focus();
25021         
25022     },
25023       
25024
25025     // private
25026     onDestroy : function(){
25027         
25028         
25029         
25030         if(this.rendered){
25031             
25032             for (var i =0; i < this.toolbars.length;i++) {
25033                 // fixme - ask toolbars for heights?
25034                 this.toolbars[i].onDestroy();
25035             }
25036             
25037             this.wrap.dom.innerHTML = '';
25038             this.wrap.remove();
25039         }
25040     },
25041
25042     // private
25043     onFirstFocus : function(){
25044         //Roo.log("onFirstFocus");
25045         this.editorcore.onFirstFocus();
25046          for (var i =0; i < this.toolbars.length;i++) {
25047             this.toolbars[i].onFirstFocus();
25048         }
25049         
25050     },
25051     
25052     // private
25053     syncValue : function()
25054     {   
25055         this.editorcore.syncValue();
25056     },
25057     
25058     pushValue : function()
25059     {   
25060         this.editorcore.pushValue();
25061     }
25062      
25063     
25064     // hide stuff that is not compatible
25065     /**
25066      * @event blur
25067      * @hide
25068      */
25069     /**
25070      * @event change
25071      * @hide
25072      */
25073     /**
25074      * @event focus
25075      * @hide
25076      */
25077     /**
25078      * @event specialkey
25079      * @hide
25080      */
25081     /**
25082      * @cfg {String} fieldClass @hide
25083      */
25084     /**
25085      * @cfg {String} focusClass @hide
25086      */
25087     /**
25088      * @cfg {String} autoCreate @hide
25089      */
25090     /**
25091      * @cfg {String} inputType @hide
25092      */
25093      
25094     /**
25095      * @cfg {String} invalidText @hide
25096      */
25097     /**
25098      * @cfg {String} msgFx @hide
25099      */
25100     /**
25101      * @cfg {String} validateOnBlur @hide
25102      */
25103 });
25104  
25105     
25106    
25107    
25108    
25109       
25110 Roo.namespace('Roo.bootstrap.htmleditor');
25111 /**
25112  * @class Roo.bootstrap.HtmlEditorToolbar1
25113  * Basic Toolbar
25114  * 
25115  * @example
25116  * Usage:
25117  *
25118  new Roo.bootstrap.HtmlEditor({
25119     ....
25120     toolbars : [
25121         new Roo.bootstrap.HtmlEditorToolbar1({
25122             disable : { fonts: 1 , format: 1, ..., ... , ...],
25123             btns : [ .... ]
25124         })
25125     }
25126      
25127  * 
25128  * @cfg {Object} disable List of elements to disable..
25129  * @cfg {Array} btns List of additional buttons.
25130  * 
25131  * 
25132  * NEEDS Extra CSS? 
25133  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25134  */
25135  
25136 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25137 {
25138     
25139     Roo.apply(this, config);
25140     
25141     // default disabled, based on 'good practice'..
25142     this.disable = this.disable || {};
25143     Roo.applyIf(this.disable, {
25144         fontSize : true,
25145         colors : true,
25146         specialElements : true
25147     });
25148     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25149     
25150     this.editor = config.editor;
25151     this.editorcore = config.editor.editorcore;
25152     
25153     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25154     
25155     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25156     // dont call parent... till later.
25157 }
25158 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25159      
25160     bar : true,
25161     
25162     editor : false,
25163     editorcore : false,
25164     
25165     
25166     formats : [
25167         "p" ,  
25168         "h1","h2","h3","h4","h5","h6", 
25169         "pre", "code", 
25170         "abbr", "acronym", "address", "cite", "samp", "var",
25171         'div','span'
25172     ],
25173     
25174     onRender : function(ct, position)
25175     {
25176        // Roo.log("Call onRender: " + this.xtype);
25177         
25178        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25179        Roo.log(this.el);
25180        this.el.dom.style.marginBottom = '0';
25181        var _this = this;
25182        var editorcore = this.editorcore;
25183        var editor= this.editor;
25184        
25185        var children = [];
25186        var btn = function(id,cmd , toggle, handler, html){
25187        
25188             var  event = toggle ? 'toggle' : 'click';
25189        
25190             var a = {
25191                 size : 'sm',
25192                 xtype: 'Button',
25193                 xns: Roo.bootstrap,
25194                 //glyphicon : id,
25195                 fa: id,
25196                 cmd : id || cmd,
25197                 enableToggle:toggle !== false,
25198                 html : html || '',
25199                 pressed : toggle ? false : null,
25200                 listeners : {}
25201             };
25202             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25203                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25204             };
25205             children.push(a);
25206             return a;
25207        }
25208        
25209     //    var cb_box = function...
25210         
25211         var style = {
25212                 xtype: 'Button',
25213                 size : 'sm',
25214                 xns: Roo.bootstrap,
25215                 fa : 'font',
25216                 //html : 'submit'
25217                 menu : {
25218                     xtype: 'Menu',
25219                     xns: Roo.bootstrap,
25220                     items:  []
25221                 }
25222         };
25223         Roo.each(this.formats, function(f) {
25224             style.menu.items.push({
25225                 xtype :'MenuItem',
25226                 xns: Roo.bootstrap,
25227                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25228                 tagname : f,
25229                 listeners : {
25230                     click : function()
25231                     {
25232                         editorcore.insertTag(this.tagname);
25233                         editor.focus();
25234                     }
25235                 }
25236                 
25237             });
25238         });
25239         children.push(style);   
25240         
25241         btn('bold',false,true);
25242         btn('italic',false,true);
25243         btn('align-left', 'justifyleft',true);
25244         btn('align-center', 'justifycenter',true);
25245         btn('align-right' , 'justifyright',true);
25246         btn('link', false, false, function(btn) {
25247             //Roo.log("create link?");
25248             var url = prompt(this.createLinkText, this.defaultLinkValue);
25249             if(url && url != 'http:/'+'/'){
25250                 this.editorcore.relayCmd('createlink', url);
25251             }
25252         }),
25253         btn('list','insertunorderedlist',true);
25254         btn('pencil', false,true, function(btn){
25255                 Roo.log(this);
25256                 this.toggleSourceEdit(btn.pressed);
25257         });
25258         
25259         if (this.editor.btns.length > 0) {
25260             for (var i = 0; i<this.editor.btns.length; i++) {
25261                 children.push(this.editor.btns[i]);
25262             }
25263         }
25264         
25265         /*
25266         var cog = {
25267                 xtype: 'Button',
25268                 size : 'sm',
25269                 xns: Roo.bootstrap,
25270                 glyphicon : 'cog',
25271                 //html : 'submit'
25272                 menu : {
25273                     xtype: 'Menu',
25274                     xns: Roo.bootstrap,
25275                     items:  []
25276                 }
25277         };
25278         
25279         cog.menu.items.push({
25280             xtype :'MenuItem',
25281             xns: Roo.bootstrap,
25282             html : Clean styles,
25283             tagname : f,
25284             listeners : {
25285                 click : function()
25286                 {
25287                     editorcore.insertTag(this.tagname);
25288                     editor.focus();
25289                 }
25290             }
25291             
25292         });
25293        */
25294         
25295          
25296        this.xtype = 'NavSimplebar';
25297         
25298         for(var i=0;i< children.length;i++) {
25299             
25300             this.buttons.add(this.addxtypeChild(children[i]));
25301             
25302         }
25303         
25304         editor.on('editorevent', this.updateToolbar, this);
25305     },
25306     onBtnClick : function(id)
25307     {
25308        this.editorcore.relayCmd(id);
25309        this.editorcore.focus();
25310     },
25311     
25312     /**
25313      * Protected method that will not generally be called directly. It triggers
25314      * a toolbar update by reading the markup state of the current selection in the editor.
25315      */
25316     updateToolbar: function(){
25317
25318         if(!this.editorcore.activated){
25319             this.editor.onFirstFocus(); // is this neeed?
25320             return;
25321         }
25322
25323         var btns = this.buttons; 
25324         var doc = this.editorcore.doc;
25325         btns.get('bold').setActive(doc.queryCommandState('bold'));
25326         btns.get('italic').setActive(doc.queryCommandState('italic'));
25327         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25328         
25329         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25330         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25331         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25332         
25333         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25334         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25335          /*
25336         
25337         var ans = this.editorcore.getAllAncestors();
25338         if (this.formatCombo) {
25339             
25340             
25341             var store = this.formatCombo.store;
25342             this.formatCombo.setValue("");
25343             for (var i =0; i < ans.length;i++) {
25344                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25345                     // select it..
25346                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25347                     break;
25348                 }
25349             }
25350         }
25351         
25352         
25353         
25354         // hides menus... - so this cant be on a menu...
25355         Roo.bootstrap.MenuMgr.hideAll();
25356         */
25357         Roo.bootstrap.MenuMgr.hideAll();
25358         //this.editorsyncValue();
25359     },
25360     onFirstFocus: function() {
25361         this.buttons.each(function(item){
25362            item.enable();
25363         });
25364     },
25365     toggleSourceEdit : function(sourceEditMode){
25366         
25367           
25368         if(sourceEditMode){
25369             Roo.log("disabling buttons");
25370            this.buttons.each( function(item){
25371                 if(item.cmd != 'pencil'){
25372                     item.disable();
25373                 }
25374             });
25375           
25376         }else{
25377             Roo.log("enabling buttons");
25378             if(this.editorcore.initialized){
25379                 this.buttons.each( function(item){
25380                     item.enable();
25381                 });
25382             }
25383             
25384         }
25385         Roo.log("calling toggole on editor");
25386         // tell the editor that it's been pressed..
25387         this.editor.toggleSourceEdit(sourceEditMode);
25388        
25389     }
25390 });
25391
25392
25393
25394
25395
25396 /**
25397  * @class Roo.bootstrap.Table.AbstractSelectionModel
25398  * @extends Roo.util.Observable
25399  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25400  * implemented by descendant classes.  This class should not be directly instantiated.
25401  * @constructor
25402  */
25403 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25404     this.locked = false;
25405     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25406 };
25407
25408
25409 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25410     /** @ignore Called by the grid automatically. Do not call directly. */
25411     init : function(grid){
25412         this.grid = grid;
25413         this.initEvents();
25414     },
25415
25416     /**
25417      * Locks the selections.
25418      */
25419     lock : function(){
25420         this.locked = true;
25421     },
25422
25423     /**
25424      * Unlocks the selections.
25425      */
25426     unlock : function(){
25427         this.locked = false;
25428     },
25429
25430     /**
25431      * Returns true if the selections are locked.
25432      * @return {Boolean}
25433      */
25434     isLocked : function(){
25435         return this.locked;
25436     },
25437     
25438     
25439     initEvents : function ()
25440     {
25441         
25442     }
25443 });
25444 /**
25445  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25446  * @class Roo.bootstrap.Table.RowSelectionModel
25447  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25448  * It supports multiple selections and keyboard selection/navigation. 
25449  * @constructor
25450  * @param {Object} config
25451  */
25452
25453 Roo.bootstrap.Table.RowSelectionModel = function(config){
25454     Roo.apply(this, config);
25455     this.selections = new Roo.util.MixedCollection(false, function(o){
25456         return o.id;
25457     });
25458
25459     this.last = false;
25460     this.lastActive = false;
25461
25462     this.addEvents({
25463         /**
25464              * @event selectionchange
25465              * Fires when the selection changes
25466              * @param {SelectionModel} this
25467              */
25468             "selectionchange" : true,
25469         /**
25470              * @event afterselectionchange
25471              * Fires after the selection changes (eg. by key press or clicking)
25472              * @param {SelectionModel} this
25473              */
25474             "afterselectionchange" : true,
25475         /**
25476              * @event beforerowselect
25477              * Fires when a row is selected being selected, return false to cancel.
25478              * @param {SelectionModel} this
25479              * @param {Number} rowIndex The selected index
25480              * @param {Boolean} keepExisting False if other selections will be cleared
25481              */
25482             "beforerowselect" : true,
25483         /**
25484              * @event rowselect
25485              * Fires when a row is selected.
25486              * @param {SelectionModel} this
25487              * @param {Number} rowIndex The selected index
25488              * @param {Roo.data.Record} r The record
25489              */
25490             "rowselect" : true,
25491         /**
25492              * @event rowdeselect
25493              * Fires when a row is deselected.
25494              * @param {SelectionModel} this
25495              * @param {Number} rowIndex The selected index
25496              */
25497         "rowdeselect" : true
25498     });
25499     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25500     this.locked = false;
25501  };
25502
25503 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25504     /**
25505      * @cfg {Boolean} singleSelect
25506      * True to allow selection of only one row at a time (defaults to false)
25507      */
25508     singleSelect : false,
25509
25510     // private
25511     initEvents : function()
25512     {
25513
25514         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25515         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25516         //}else{ // allow click to work like normal
25517          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25518         //}
25519         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25520         this.grid.on("rowclick", this.handleMouseDown, this);
25521         
25522         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25523             "up" : function(e){
25524                 if(!e.shiftKey){
25525                     this.selectPrevious(e.shiftKey);
25526                 }else if(this.last !== false && this.lastActive !== false){
25527                     var last = this.last;
25528                     this.selectRange(this.last,  this.lastActive-1);
25529                     this.grid.getView().focusRow(this.lastActive);
25530                     if(last !== false){
25531                         this.last = last;
25532                     }
25533                 }else{
25534                     this.selectFirstRow();
25535                 }
25536                 this.fireEvent("afterselectionchange", this);
25537             },
25538             "down" : function(e){
25539                 if(!e.shiftKey){
25540                     this.selectNext(e.shiftKey);
25541                 }else if(this.last !== false && this.lastActive !== false){
25542                     var last = this.last;
25543                     this.selectRange(this.last,  this.lastActive+1);
25544                     this.grid.getView().focusRow(this.lastActive);
25545                     if(last !== false){
25546                         this.last = last;
25547                     }
25548                 }else{
25549                     this.selectFirstRow();
25550                 }
25551                 this.fireEvent("afterselectionchange", this);
25552             },
25553             scope: this
25554         });
25555         this.grid.store.on('load', function(){
25556             this.selections.clear();
25557         },this);
25558         /*
25559         var view = this.grid.view;
25560         view.on("refresh", this.onRefresh, this);
25561         view.on("rowupdated", this.onRowUpdated, this);
25562         view.on("rowremoved", this.onRemove, this);
25563         */
25564     },
25565
25566     // private
25567     onRefresh : function()
25568     {
25569         var ds = this.grid.store, i, v = this.grid.view;
25570         var s = this.selections;
25571         s.each(function(r){
25572             if((i = ds.indexOfId(r.id)) != -1){
25573                 v.onRowSelect(i);
25574             }else{
25575                 s.remove(r);
25576             }
25577         });
25578     },
25579
25580     // private
25581     onRemove : function(v, index, r){
25582         this.selections.remove(r);
25583     },
25584
25585     // private
25586     onRowUpdated : function(v, index, r){
25587         if(this.isSelected(r)){
25588             v.onRowSelect(index);
25589         }
25590     },
25591
25592     /**
25593      * Select records.
25594      * @param {Array} records The records to select
25595      * @param {Boolean} keepExisting (optional) True to keep existing selections
25596      */
25597     selectRecords : function(records, keepExisting)
25598     {
25599         if(!keepExisting){
25600             this.clearSelections();
25601         }
25602             var ds = this.grid.store;
25603         for(var i = 0, len = records.length; i < len; i++){
25604             this.selectRow(ds.indexOf(records[i]), true);
25605         }
25606     },
25607
25608     /**
25609      * Gets the number of selected rows.
25610      * @return {Number}
25611      */
25612     getCount : function(){
25613         return this.selections.length;
25614     },
25615
25616     /**
25617      * Selects the first row in the grid.
25618      */
25619     selectFirstRow : function(){
25620         this.selectRow(0);
25621     },
25622
25623     /**
25624      * Select the last row.
25625      * @param {Boolean} keepExisting (optional) True to keep existing selections
25626      */
25627     selectLastRow : function(keepExisting){
25628         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25629         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25630     },
25631
25632     /**
25633      * Selects the row immediately following the last selected row.
25634      * @param {Boolean} keepExisting (optional) True to keep existing selections
25635      */
25636     selectNext : function(keepExisting)
25637     {
25638             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25639             this.selectRow(this.last+1, keepExisting);
25640             this.grid.getView().focusRow(this.last);
25641         }
25642     },
25643
25644     /**
25645      * Selects the row that precedes the last selected row.
25646      * @param {Boolean} keepExisting (optional) True to keep existing selections
25647      */
25648     selectPrevious : function(keepExisting){
25649         if(this.last){
25650             this.selectRow(this.last-1, keepExisting);
25651             this.grid.getView().focusRow(this.last);
25652         }
25653     },
25654
25655     /**
25656      * Returns the selected records
25657      * @return {Array} Array of selected records
25658      */
25659     getSelections : function(){
25660         return [].concat(this.selections.items);
25661     },
25662
25663     /**
25664      * Returns the first selected record.
25665      * @return {Record}
25666      */
25667     getSelected : function(){
25668         return this.selections.itemAt(0);
25669     },
25670
25671
25672     /**
25673      * Clears all selections.
25674      */
25675     clearSelections : function(fast)
25676     {
25677         if(this.locked) {
25678             return;
25679         }
25680         if(fast !== true){
25681                 var ds = this.grid.store;
25682             var s = this.selections;
25683             s.each(function(r){
25684                 this.deselectRow(ds.indexOfId(r.id));
25685             }, this);
25686             s.clear();
25687         }else{
25688             this.selections.clear();
25689         }
25690         this.last = false;
25691     },
25692
25693
25694     /**
25695      * Selects all rows.
25696      */
25697     selectAll : function(){
25698         if(this.locked) {
25699             return;
25700         }
25701         this.selections.clear();
25702         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25703             this.selectRow(i, true);
25704         }
25705     },
25706
25707     /**
25708      * Returns True if there is a selection.
25709      * @return {Boolean}
25710      */
25711     hasSelection : function(){
25712         return this.selections.length > 0;
25713     },
25714
25715     /**
25716      * Returns True if the specified row is selected.
25717      * @param {Number/Record} record The record or index of the record to check
25718      * @return {Boolean}
25719      */
25720     isSelected : function(index){
25721             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25722         return (r && this.selections.key(r.id) ? true : false);
25723     },
25724
25725     /**
25726      * Returns True if the specified record id is selected.
25727      * @param {String} id The id of record to check
25728      * @return {Boolean}
25729      */
25730     isIdSelected : function(id){
25731         return (this.selections.key(id) ? true : false);
25732     },
25733
25734
25735     // private
25736     handleMouseDBClick : function(e, t){
25737         
25738     },
25739     // private
25740     handleMouseDown : function(e, t)
25741     {
25742             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25743         if(this.isLocked() || rowIndex < 0 ){
25744             return;
25745         };
25746         if(e.shiftKey && this.last !== false){
25747             var last = this.last;
25748             this.selectRange(last, rowIndex, e.ctrlKey);
25749             this.last = last; // reset the last
25750             t.focus();
25751     
25752         }else{
25753             var isSelected = this.isSelected(rowIndex);
25754             //Roo.log("select row:" + rowIndex);
25755             if(isSelected){
25756                 this.deselectRow(rowIndex);
25757             } else {
25758                         this.selectRow(rowIndex, true);
25759             }
25760     
25761             /*
25762                 if(e.button !== 0 && isSelected){
25763                 alert('rowIndex 2: ' + rowIndex);
25764                     view.focusRow(rowIndex);
25765                 }else if(e.ctrlKey && isSelected){
25766                     this.deselectRow(rowIndex);
25767                 }else if(!isSelected){
25768                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25769                     view.focusRow(rowIndex);
25770                 }
25771             */
25772         }
25773         this.fireEvent("afterselectionchange", this);
25774     },
25775     // private
25776     handleDragableRowClick :  function(grid, rowIndex, e) 
25777     {
25778         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25779             this.selectRow(rowIndex, false);
25780             grid.view.focusRow(rowIndex);
25781              this.fireEvent("afterselectionchange", this);
25782         }
25783     },
25784     
25785     /**
25786      * Selects multiple rows.
25787      * @param {Array} rows Array of the indexes of the row to select
25788      * @param {Boolean} keepExisting (optional) True to keep existing selections
25789      */
25790     selectRows : function(rows, keepExisting){
25791         if(!keepExisting){
25792             this.clearSelections();
25793         }
25794         for(var i = 0, len = rows.length; i < len; i++){
25795             this.selectRow(rows[i], true);
25796         }
25797     },
25798
25799     /**
25800      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25801      * @param {Number} startRow The index of the first row in the range
25802      * @param {Number} endRow The index of the last row in the range
25803      * @param {Boolean} keepExisting (optional) True to retain existing selections
25804      */
25805     selectRange : function(startRow, endRow, keepExisting){
25806         if(this.locked) {
25807             return;
25808         }
25809         if(!keepExisting){
25810             this.clearSelections();
25811         }
25812         if(startRow <= endRow){
25813             for(var i = startRow; i <= endRow; i++){
25814                 this.selectRow(i, true);
25815             }
25816         }else{
25817             for(var i = startRow; i >= endRow; i--){
25818                 this.selectRow(i, true);
25819             }
25820         }
25821     },
25822
25823     /**
25824      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25825      * @param {Number} startRow The index of the first row in the range
25826      * @param {Number} endRow The index of the last row in the range
25827      */
25828     deselectRange : function(startRow, endRow, preventViewNotify){
25829         if(this.locked) {
25830             return;
25831         }
25832         for(var i = startRow; i <= endRow; i++){
25833             this.deselectRow(i, preventViewNotify);
25834         }
25835     },
25836
25837     /**
25838      * Selects a row.
25839      * @param {Number} row The index of the row to select
25840      * @param {Boolean} keepExisting (optional) True to keep existing selections
25841      */
25842     selectRow : function(index, keepExisting, preventViewNotify)
25843     {
25844             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25845             return;
25846         }
25847         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25848             if(!keepExisting || this.singleSelect){
25849                 this.clearSelections();
25850             }
25851             
25852             var r = this.grid.store.getAt(index);
25853             //console.log('selectRow - record id :' + r.id);
25854             
25855             this.selections.add(r);
25856             this.last = this.lastActive = index;
25857             if(!preventViewNotify){
25858                 var proxy = new Roo.Element(
25859                                 this.grid.getRowDom(index)
25860                 );
25861                 proxy.addClass('bg-info info');
25862             }
25863             this.fireEvent("rowselect", this, index, r);
25864             this.fireEvent("selectionchange", this);
25865         }
25866     },
25867
25868     /**
25869      * Deselects a row.
25870      * @param {Number} row The index of the row to deselect
25871      */
25872     deselectRow : function(index, preventViewNotify)
25873     {
25874         if(this.locked) {
25875             return;
25876         }
25877         if(this.last == index){
25878             this.last = false;
25879         }
25880         if(this.lastActive == index){
25881             this.lastActive = false;
25882         }
25883         
25884         var r = this.grid.store.getAt(index);
25885         if (!r) {
25886             return;
25887         }
25888         
25889         this.selections.remove(r);
25890         //.console.log('deselectRow - record id :' + r.id);
25891         if(!preventViewNotify){
25892         
25893             var proxy = new Roo.Element(
25894                 this.grid.getRowDom(index)
25895             );
25896             proxy.removeClass('bg-info info');
25897         }
25898         this.fireEvent("rowdeselect", this, index);
25899         this.fireEvent("selectionchange", this);
25900     },
25901
25902     // private
25903     restoreLast : function(){
25904         if(this._last){
25905             this.last = this._last;
25906         }
25907     },
25908
25909     // private
25910     acceptsNav : function(row, col, cm){
25911         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25912     },
25913
25914     // private
25915     onEditorKey : function(field, e){
25916         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25917         if(k == e.TAB){
25918             e.stopEvent();
25919             ed.completeEdit();
25920             if(e.shiftKey){
25921                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25922             }else{
25923                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25924             }
25925         }else if(k == e.ENTER && !e.ctrlKey){
25926             e.stopEvent();
25927             ed.completeEdit();
25928             if(e.shiftKey){
25929                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25930             }else{
25931                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25932             }
25933         }else if(k == e.ESC){
25934             ed.cancelEdit();
25935         }
25936         if(newCell){
25937             g.startEditing(newCell[0], newCell[1]);
25938         }
25939     }
25940 });
25941 /*
25942  * Based on:
25943  * Ext JS Library 1.1.1
25944  * Copyright(c) 2006-2007, Ext JS, LLC.
25945  *
25946  * Originally Released Under LGPL - original licence link has changed is not relivant.
25947  *
25948  * Fork - LGPL
25949  * <script type="text/javascript">
25950  */
25951  
25952 /**
25953  * @class Roo.bootstrap.PagingToolbar
25954  * @extends Roo.bootstrap.NavSimplebar
25955  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25956  * @constructor
25957  * Create a new PagingToolbar
25958  * @param {Object} config The config object
25959  * @param {Roo.data.Store} store
25960  */
25961 Roo.bootstrap.PagingToolbar = function(config)
25962 {
25963     // old args format still supported... - xtype is prefered..
25964         // created from xtype...
25965     
25966     this.ds = config.dataSource;
25967     
25968     if (config.store && !this.ds) {
25969         this.store= Roo.factory(config.store, Roo.data);
25970         this.ds = this.store;
25971         this.ds.xmodule = this.xmodule || false;
25972     }
25973     
25974     this.toolbarItems = [];
25975     if (config.items) {
25976         this.toolbarItems = config.items;
25977     }
25978     
25979     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25980     
25981     this.cursor = 0;
25982     
25983     if (this.ds) { 
25984         this.bind(this.ds);
25985     }
25986     
25987     if (Roo.bootstrap.version == 4) {
25988         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25989     } else {
25990         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25991     }
25992     
25993 };
25994
25995 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25996     /**
25997      * @cfg {Roo.data.Store} dataSource
25998      * The underlying data store providing the paged data
25999      */
26000     /**
26001      * @cfg {String/HTMLElement/Element} container
26002      * container The id or element that will contain the toolbar
26003      */
26004     /**
26005      * @cfg {Boolean} displayInfo
26006      * True to display the displayMsg (defaults to false)
26007      */
26008     /**
26009      * @cfg {Number} pageSize
26010      * The number of records to display per page (defaults to 20)
26011      */
26012     pageSize: 20,
26013     /**
26014      * @cfg {String} displayMsg
26015      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26016      */
26017     displayMsg : 'Displaying {0} - {1} of {2}',
26018     /**
26019      * @cfg {String} emptyMsg
26020      * The message to display when no records are found (defaults to "No data to display")
26021      */
26022     emptyMsg : 'No data to display',
26023     /**
26024      * Customizable piece of the default paging text (defaults to "Page")
26025      * @type String
26026      */
26027     beforePageText : "Page",
26028     /**
26029      * Customizable piece of the default paging text (defaults to "of %0")
26030      * @type String
26031      */
26032     afterPageText : "of {0}",
26033     /**
26034      * Customizable piece of the default paging text (defaults to "First Page")
26035      * @type String
26036      */
26037     firstText : "First Page",
26038     /**
26039      * Customizable piece of the default paging text (defaults to "Previous Page")
26040      * @type String
26041      */
26042     prevText : "Previous Page",
26043     /**
26044      * Customizable piece of the default paging text (defaults to "Next Page")
26045      * @type String
26046      */
26047     nextText : "Next Page",
26048     /**
26049      * Customizable piece of the default paging text (defaults to "Last Page")
26050      * @type String
26051      */
26052     lastText : "Last Page",
26053     /**
26054      * Customizable piece of the default paging text (defaults to "Refresh")
26055      * @type String
26056      */
26057     refreshText : "Refresh",
26058
26059     buttons : false,
26060     // private
26061     onRender : function(ct, position) 
26062     {
26063         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26064         this.navgroup.parentId = this.id;
26065         this.navgroup.onRender(this.el, null);
26066         // add the buttons to the navgroup
26067         
26068         if(this.displayInfo){
26069             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26070             this.displayEl = this.el.select('.x-paging-info', true).first();
26071 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26072 //            this.displayEl = navel.el.select('span',true).first();
26073         }
26074         
26075         var _this = this;
26076         
26077         if(this.buttons){
26078             Roo.each(_this.buttons, function(e){ // this might need to use render????
26079                Roo.factory(e).render(_this.el);
26080             });
26081         }
26082             
26083         Roo.each(_this.toolbarItems, function(e) {
26084             _this.navgroup.addItem(e);
26085         });
26086         
26087         
26088         this.first = this.navgroup.addItem({
26089             tooltip: this.firstText,
26090             cls: "prev btn-outline-secondary",
26091             html : ' <i class="fa fa-step-backward"></i>',
26092             disabled: true,
26093             preventDefault: true,
26094             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26095         });
26096         
26097         this.prev =  this.navgroup.addItem({
26098             tooltip: this.prevText,
26099             cls: "prev btn-outline-secondary",
26100             html : ' <i class="fa fa-backward"></i>',
26101             disabled: true,
26102             preventDefault: true,
26103             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26104         });
26105     //this.addSeparator();
26106         
26107         
26108         var field = this.navgroup.addItem( {
26109             tagtype : 'span',
26110             cls : 'x-paging-position  btn-outline-secondary',
26111              disabled: true,
26112             html : this.beforePageText  +
26113                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26114                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26115          } ); //?? escaped?
26116         
26117         this.field = field.el.select('input', true).first();
26118         this.field.on("keydown", this.onPagingKeydown, this);
26119         this.field.on("focus", function(){this.dom.select();});
26120     
26121     
26122         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26123         //this.field.setHeight(18);
26124         //this.addSeparator();
26125         this.next = this.navgroup.addItem({
26126             tooltip: this.nextText,
26127             cls: "next btn-outline-secondary",
26128             html : ' <i class="fa fa-forward"></i>',
26129             disabled: true,
26130             preventDefault: true,
26131             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26132         });
26133         this.last = this.navgroup.addItem({
26134             tooltip: this.lastText,
26135             html : ' <i class="fa fa-step-forward"></i>',
26136             cls: "next btn-outline-secondary",
26137             disabled: true,
26138             preventDefault: true,
26139             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26140         });
26141     //this.addSeparator();
26142         this.loading = this.navgroup.addItem({
26143             tooltip: this.refreshText,
26144             cls: "btn-outline-secondary",
26145             html : ' <i class="fa fa-refresh"></i>',
26146             preventDefault: true,
26147             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26148         });
26149         
26150     },
26151
26152     // private
26153     updateInfo : function(){
26154         if(this.displayEl){
26155             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26156             var msg = count == 0 ?
26157                 this.emptyMsg :
26158                 String.format(
26159                     this.displayMsg,
26160                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26161                 );
26162             this.displayEl.update(msg);
26163         }
26164     },
26165
26166     // private
26167     onLoad : function(ds, r, o)
26168     {
26169         this.cursor = o.params.start ? o.params.start : 0;
26170         
26171         var d = this.getPageData(),
26172             ap = d.activePage,
26173             ps = d.pages;
26174         
26175         
26176         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26177         this.field.dom.value = ap;
26178         this.first.setDisabled(ap == 1);
26179         this.prev.setDisabled(ap == 1);
26180         this.next.setDisabled(ap == ps);
26181         this.last.setDisabled(ap == ps);
26182         this.loading.enable();
26183         this.updateInfo();
26184     },
26185
26186     // private
26187     getPageData : function(){
26188         var total = this.ds.getTotalCount();
26189         return {
26190             total : total,
26191             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26192             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26193         };
26194     },
26195
26196     // private
26197     onLoadError : function(){
26198         this.loading.enable();
26199     },
26200
26201     // private
26202     onPagingKeydown : function(e){
26203         var k = e.getKey();
26204         var d = this.getPageData();
26205         if(k == e.RETURN){
26206             var v = this.field.dom.value, pageNum;
26207             if(!v || isNaN(pageNum = parseInt(v, 10))){
26208                 this.field.dom.value = d.activePage;
26209                 return;
26210             }
26211             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26212             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26213             e.stopEvent();
26214         }
26215         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))
26216         {
26217           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26218           this.field.dom.value = pageNum;
26219           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26220           e.stopEvent();
26221         }
26222         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26223         {
26224           var v = this.field.dom.value, pageNum; 
26225           var increment = (e.shiftKey) ? 10 : 1;
26226           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26227                 increment *= -1;
26228           }
26229           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26230             this.field.dom.value = d.activePage;
26231             return;
26232           }
26233           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26234           {
26235             this.field.dom.value = parseInt(v, 10) + increment;
26236             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26237             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26238           }
26239           e.stopEvent();
26240         }
26241     },
26242
26243     // private
26244     beforeLoad : function(){
26245         if(this.loading){
26246             this.loading.disable();
26247         }
26248     },
26249
26250     // private
26251     onClick : function(which){
26252         
26253         var ds = this.ds;
26254         if (!ds) {
26255             return;
26256         }
26257         
26258         switch(which){
26259             case "first":
26260                 ds.load({params:{start: 0, limit: this.pageSize}});
26261             break;
26262             case "prev":
26263                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26264             break;
26265             case "next":
26266                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26267             break;
26268             case "last":
26269                 var total = ds.getTotalCount();
26270                 var extra = total % this.pageSize;
26271                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26272                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26273             break;
26274             case "refresh":
26275                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26276             break;
26277         }
26278     },
26279
26280     /**
26281      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26282      * @param {Roo.data.Store} store The data store to unbind
26283      */
26284     unbind : function(ds){
26285         ds.un("beforeload", this.beforeLoad, this);
26286         ds.un("load", this.onLoad, this);
26287         ds.un("loadexception", this.onLoadError, this);
26288         ds.un("remove", this.updateInfo, this);
26289         ds.un("add", this.updateInfo, this);
26290         this.ds = undefined;
26291     },
26292
26293     /**
26294      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26295      * @param {Roo.data.Store} store The data store to bind
26296      */
26297     bind : function(ds){
26298         ds.on("beforeload", this.beforeLoad, this);
26299         ds.on("load", this.onLoad, this);
26300         ds.on("loadexception", this.onLoadError, this);
26301         ds.on("remove", this.updateInfo, this);
26302         ds.on("add", this.updateInfo, this);
26303         this.ds = ds;
26304     }
26305 });/*
26306  * - LGPL
26307  *
26308  * element
26309  * 
26310  */
26311
26312 /**
26313  * @class Roo.bootstrap.MessageBar
26314  * @extends Roo.bootstrap.Component
26315  * Bootstrap MessageBar class
26316  * @cfg {String} html contents of the MessageBar
26317  * @cfg {String} weight (info | success | warning | danger) default info
26318  * @cfg {String} beforeClass insert the bar before the given class
26319  * @cfg {Boolean} closable (true | false) default false
26320  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26321  * 
26322  * @constructor
26323  * Create a new Element
26324  * @param {Object} config The config object
26325  */
26326
26327 Roo.bootstrap.MessageBar = function(config){
26328     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26329 };
26330
26331 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26332     
26333     html: '',
26334     weight: 'info',
26335     closable: false,
26336     fixed: false,
26337     beforeClass: 'bootstrap-sticky-wrap',
26338     
26339     getAutoCreate : function(){
26340         
26341         var cfg = {
26342             tag: 'div',
26343             cls: 'alert alert-dismissable alert-' + this.weight,
26344             cn: [
26345                 {
26346                     tag: 'span',
26347                     cls: 'message',
26348                     html: this.html || ''
26349                 }
26350             ]
26351         };
26352         
26353         if(this.fixed){
26354             cfg.cls += ' alert-messages-fixed';
26355         }
26356         
26357         if(this.closable){
26358             cfg.cn.push({
26359                 tag: 'button',
26360                 cls: 'close',
26361                 html: 'x'
26362             });
26363         }
26364         
26365         return cfg;
26366     },
26367     
26368     onRender : function(ct, position)
26369     {
26370         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26371         
26372         if(!this.el){
26373             var cfg = Roo.apply({},  this.getAutoCreate());
26374             cfg.id = Roo.id();
26375             
26376             if (this.cls) {
26377                 cfg.cls += ' ' + this.cls;
26378             }
26379             if (this.style) {
26380                 cfg.style = this.style;
26381             }
26382             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26383             
26384             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26385         }
26386         
26387         this.el.select('>button.close').on('click', this.hide, this);
26388         
26389     },
26390     
26391     show : function()
26392     {
26393         if (!this.rendered) {
26394             this.render();
26395         }
26396         
26397         this.el.show();
26398         
26399         this.fireEvent('show', this);
26400         
26401     },
26402     
26403     hide : function()
26404     {
26405         if (!this.rendered) {
26406             this.render();
26407         }
26408         
26409         this.el.hide();
26410         
26411         this.fireEvent('hide', this);
26412     },
26413     
26414     update : function()
26415     {
26416 //        var e = this.el.dom.firstChild;
26417 //        
26418 //        if(this.closable){
26419 //            e = e.nextSibling;
26420 //        }
26421 //        
26422 //        e.data = this.html || '';
26423
26424         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26425     }
26426    
26427 });
26428
26429  
26430
26431      /*
26432  * - LGPL
26433  *
26434  * Graph
26435  * 
26436  */
26437
26438
26439 /**
26440  * @class Roo.bootstrap.Graph
26441  * @extends Roo.bootstrap.Component
26442  * Bootstrap Graph class
26443 > Prameters
26444  -sm {number} sm 4
26445  -md {number} md 5
26446  @cfg {String} graphtype  bar | vbar | pie
26447  @cfg {number} g_x coodinator | centre x (pie)
26448  @cfg {number} g_y coodinator | centre y (pie)
26449  @cfg {number} g_r radius (pie)
26450  @cfg {number} g_height height of the chart (respected by all elements in the set)
26451  @cfg {number} g_width width of the chart (respected by all elements in the set)
26452  @cfg {Object} title The title of the chart
26453     
26454  -{Array}  values
26455  -opts (object) options for the chart 
26456      o {
26457      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26458      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26459      o vgutter (number)
26460      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.
26461      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26462      o to
26463      o stretch (boolean)
26464      o }
26465  -opts (object) options for the pie
26466      o{
26467      o cut
26468      o startAngle (number)
26469      o endAngle (number)
26470      } 
26471  *
26472  * @constructor
26473  * Create a new Input
26474  * @param {Object} config The config object
26475  */
26476
26477 Roo.bootstrap.Graph = function(config){
26478     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26479     
26480     this.addEvents({
26481         // img events
26482         /**
26483          * @event click
26484          * The img click event for the img.
26485          * @param {Roo.EventObject} e
26486          */
26487         "click" : true
26488     });
26489 };
26490
26491 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26492     
26493     sm: 4,
26494     md: 5,
26495     graphtype: 'bar',
26496     g_height: 250,
26497     g_width: 400,
26498     g_x: 50,
26499     g_y: 50,
26500     g_r: 30,
26501     opts:{
26502         //g_colors: this.colors,
26503         g_type: 'soft',
26504         g_gutter: '20%'
26505
26506     },
26507     title : false,
26508
26509     getAutoCreate : function(){
26510         
26511         var cfg = {
26512             tag: 'div',
26513             html : null
26514         };
26515         
26516         
26517         return  cfg;
26518     },
26519
26520     onRender : function(ct,position){
26521         
26522         
26523         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26524         
26525         if (typeof(Raphael) == 'undefined') {
26526             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26527             return;
26528         }
26529         
26530         this.raphael = Raphael(this.el.dom);
26531         
26532                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26533                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26534                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26535                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26536                 /*
26537                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26538                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26539                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26540                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26541                 
26542                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26543                 r.barchart(330, 10, 300, 220, data1);
26544                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26545                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26546                 */
26547                 
26548                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26549                 // r.barchart(30, 30, 560, 250,  xdata, {
26550                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26551                 //     axis : "0 0 1 1",
26552                 //     axisxlabels :  xdata
26553                 //     //yvalues : cols,
26554                    
26555                 // });
26556 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26557 //        
26558 //        this.load(null,xdata,{
26559 //                axis : "0 0 1 1",
26560 //                axisxlabels :  xdata
26561 //                });
26562
26563     },
26564
26565     load : function(graphtype,xdata,opts)
26566     {
26567         this.raphael.clear();
26568         if(!graphtype) {
26569             graphtype = this.graphtype;
26570         }
26571         if(!opts){
26572             opts = this.opts;
26573         }
26574         var r = this.raphael,
26575             fin = function () {
26576                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26577             },
26578             fout = function () {
26579                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26580             },
26581             pfin = function() {
26582                 this.sector.stop();
26583                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26584
26585                 if (this.label) {
26586                     this.label[0].stop();
26587                     this.label[0].attr({ r: 7.5 });
26588                     this.label[1].attr({ "font-weight": 800 });
26589                 }
26590             },
26591             pfout = function() {
26592                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26593
26594                 if (this.label) {
26595                     this.label[0].animate({ r: 5 }, 500, "bounce");
26596                     this.label[1].attr({ "font-weight": 400 });
26597                 }
26598             };
26599
26600         switch(graphtype){
26601             case 'bar':
26602                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26603                 break;
26604             case 'hbar':
26605                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26606                 break;
26607             case 'pie':
26608 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26609 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26610 //            
26611                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26612                 
26613                 break;
26614
26615         }
26616         
26617         if(this.title){
26618             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26619         }
26620         
26621     },
26622     
26623     setTitle: function(o)
26624     {
26625         this.title = o;
26626     },
26627     
26628     initEvents: function() {
26629         
26630         if(!this.href){
26631             this.el.on('click', this.onClick, this);
26632         }
26633     },
26634     
26635     onClick : function(e)
26636     {
26637         Roo.log('img onclick');
26638         this.fireEvent('click', this, e);
26639     }
26640    
26641 });
26642
26643  
26644 /*
26645  * - LGPL
26646  *
26647  * numberBox
26648  * 
26649  */
26650 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26651
26652 /**
26653  * @class Roo.bootstrap.dash.NumberBox
26654  * @extends Roo.bootstrap.Component
26655  * Bootstrap NumberBox class
26656  * @cfg {String} headline Box headline
26657  * @cfg {String} content Box content
26658  * @cfg {String} icon Box icon
26659  * @cfg {String} footer Footer text
26660  * @cfg {String} fhref Footer href
26661  * 
26662  * @constructor
26663  * Create a new NumberBox
26664  * @param {Object} config The config object
26665  */
26666
26667
26668 Roo.bootstrap.dash.NumberBox = function(config){
26669     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26670     
26671 };
26672
26673 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26674     
26675     headline : '',
26676     content : '',
26677     icon : '',
26678     footer : '',
26679     fhref : '',
26680     ficon : '',
26681     
26682     getAutoCreate : function(){
26683         
26684         var cfg = {
26685             tag : 'div',
26686             cls : 'small-box ',
26687             cn : [
26688                 {
26689                     tag : 'div',
26690                     cls : 'inner',
26691                     cn :[
26692                         {
26693                             tag : 'h3',
26694                             cls : 'roo-headline',
26695                             html : this.headline
26696                         },
26697                         {
26698                             tag : 'p',
26699                             cls : 'roo-content',
26700                             html : this.content
26701                         }
26702                     ]
26703                 }
26704             ]
26705         };
26706         
26707         if(this.icon){
26708             cfg.cn.push({
26709                 tag : 'div',
26710                 cls : 'icon',
26711                 cn :[
26712                     {
26713                         tag : 'i',
26714                         cls : 'ion ' + this.icon
26715                     }
26716                 ]
26717             });
26718         }
26719         
26720         if(this.footer){
26721             var footer = {
26722                 tag : 'a',
26723                 cls : 'small-box-footer',
26724                 href : this.fhref || '#',
26725                 html : this.footer
26726             };
26727             
26728             cfg.cn.push(footer);
26729             
26730         }
26731         
26732         return  cfg;
26733     },
26734
26735     onRender : function(ct,position){
26736         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26737
26738
26739        
26740                 
26741     },
26742
26743     setHeadline: function (value)
26744     {
26745         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26746     },
26747     
26748     setFooter: function (value, href)
26749     {
26750         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26751         
26752         if(href){
26753             this.el.select('a.small-box-footer',true).first().attr('href', href);
26754         }
26755         
26756     },
26757
26758     setContent: function (value)
26759     {
26760         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26761     },
26762
26763     initEvents: function() 
26764     {   
26765         
26766     }
26767     
26768 });
26769
26770  
26771 /*
26772  * - LGPL
26773  *
26774  * TabBox
26775  * 
26776  */
26777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26778
26779 /**
26780  * @class Roo.bootstrap.dash.TabBox
26781  * @extends Roo.bootstrap.Component
26782  * Bootstrap TabBox class
26783  * @cfg {String} title Title of the TabBox
26784  * @cfg {String} icon Icon of the TabBox
26785  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26786  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26787  * 
26788  * @constructor
26789  * Create a new TabBox
26790  * @param {Object} config The config object
26791  */
26792
26793
26794 Roo.bootstrap.dash.TabBox = function(config){
26795     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26796     this.addEvents({
26797         // raw events
26798         /**
26799          * @event addpane
26800          * When a pane is added
26801          * @param {Roo.bootstrap.dash.TabPane} pane
26802          */
26803         "addpane" : true,
26804         /**
26805          * @event activatepane
26806          * When a pane is activated
26807          * @param {Roo.bootstrap.dash.TabPane} pane
26808          */
26809         "activatepane" : true
26810         
26811          
26812     });
26813     
26814     this.panes = [];
26815 };
26816
26817 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26818
26819     title : '',
26820     icon : false,
26821     showtabs : true,
26822     tabScrollable : false,
26823     
26824     getChildContainer : function()
26825     {
26826         return this.el.select('.tab-content', true).first();
26827     },
26828     
26829     getAutoCreate : function(){
26830         
26831         var header = {
26832             tag: 'li',
26833             cls: 'pull-left header',
26834             html: this.title,
26835             cn : []
26836         };
26837         
26838         if(this.icon){
26839             header.cn.push({
26840                 tag: 'i',
26841                 cls: 'fa ' + this.icon
26842             });
26843         }
26844         
26845         var h = {
26846             tag: 'ul',
26847             cls: 'nav nav-tabs pull-right',
26848             cn: [
26849                 header
26850             ]
26851         };
26852         
26853         if(this.tabScrollable){
26854             h = {
26855                 tag: 'div',
26856                 cls: 'tab-header',
26857                 cn: [
26858                     {
26859                         tag: 'ul',
26860                         cls: 'nav nav-tabs pull-right',
26861                         cn: [
26862                             header
26863                         ]
26864                     }
26865                 ]
26866             };
26867         }
26868         
26869         var cfg = {
26870             tag: 'div',
26871             cls: 'nav-tabs-custom',
26872             cn: [
26873                 h,
26874                 {
26875                     tag: 'div',
26876                     cls: 'tab-content no-padding',
26877                     cn: []
26878                 }
26879             ]
26880         };
26881
26882         return  cfg;
26883     },
26884     initEvents : function()
26885     {
26886         //Roo.log('add add pane handler');
26887         this.on('addpane', this.onAddPane, this);
26888     },
26889      /**
26890      * Updates the box title
26891      * @param {String} html to set the title to.
26892      */
26893     setTitle : function(value)
26894     {
26895         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26896     },
26897     onAddPane : function(pane)
26898     {
26899         this.panes.push(pane);
26900         //Roo.log('addpane');
26901         //Roo.log(pane);
26902         // tabs are rendere left to right..
26903         if(!this.showtabs){
26904             return;
26905         }
26906         
26907         var ctr = this.el.select('.nav-tabs', true).first();
26908          
26909          
26910         var existing = ctr.select('.nav-tab',true);
26911         var qty = existing.getCount();;
26912         
26913         
26914         var tab = ctr.createChild({
26915             tag : 'li',
26916             cls : 'nav-tab' + (qty ? '' : ' active'),
26917             cn : [
26918                 {
26919                     tag : 'a',
26920                     href:'#',
26921                     html : pane.title
26922                 }
26923             ]
26924         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26925         pane.tab = tab;
26926         
26927         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26928         if (!qty) {
26929             pane.el.addClass('active');
26930         }
26931         
26932                 
26933     },
26934     onTabClick : function(ev,un,ob,pane)
26935     {
26936         //Roo.log('tab - prev default');
26937         ev.preventDefault();
26938         
26939         
26940         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26941         pane.tab.addClass('active');
26942         //Roo.log(pane.title);
26943         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26944         // technically we should have a deactivate event.. but maybe add later.
26945         // and it should not de-activate the selected tab...
26946         this.fireEvent('activatepane', pane);
26947         pane.el.addClass('active');
26948         pane.fireEvent('activate');
26949         
26950         
26951     },
26952     
26953     getActivePane : function()
26954     {
26955         var r = false;
26956         Roo.each(this.panes, function(p) {
26957             if(p.el.hasClass('active')){
26958                 r = p;
26959                 return false;
26960             }
26961             
26962             return;
26963         });
26964         
26965         return r;
26966     }
26967     
26968     
26969 });
26970
26971  
26972 /*
26973  * - LGPL
26974  *
26975  * Tab pane
26976  * 
26977  */
26978 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26979 /**
26980  * @class Roo.bootstrap.TabPane
26981  * @extends Roo.bootstrap.Component
26982  * Bootstrap TabPane class
26983  * @cfg {Boolean} active (false | true) Default false
26984  * @cfg {String} title title of panel
26985
26986  * 
26987  * @constructor
26988  * Create a new TabPane
26989  * @param {Object} config The config object
26990  */
26991
26992 Roo.bootstrap.dash.TabPane = function(config){
26993     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26994     
26995     this.addEvents({
26996         // raw events
26997         /**
26998          * @event activate
26999          * When a pane is activated
27000          * @param {Roo.bootstrap.dash.TabPane} pane
27001          */
27002         "activate" : true
27003          
27004     });
27005 };
27006
27007 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27008     
27009     active : false,
27010     title : '',
27011     
27012     // the tabBox that this is attached to.
27013     tab : false,
27014      
27015     getAutoCreate : function() 
27016     {
27017         var cfg = {
27018             tag: 'div',
27019             cls: 'tab-pane'
27020         };
27021         
27022         if(this.active){
27023             cfg.cls += ' active';
27024         }
27025         
27026         return cfg;
27027     },
27028     initEvents  : function()
27029     {
27030         //Roo.log('trigger add pane handler');
27031         this.parent().fireEvent('addpane', this)
27032     },
27033     
27034      /**
27035      * Updates the tab title 
27036      * @param {String} html to set the title to.
27037      */
27038     setTitle: function(str)
27039     {
27040         if (!this.tab) {
27041             return;
27042         }
27043         this.title = str;
27044         this.tab.select('a', true).first().dom.innerHTML = str;
27045         
27046     }
27047     
27048     
27049     
27050 });
27051
27052  
27053
27054
27055  /*
27056  * - LGPL
27057  *
27058  * menu
27059  * 
27060  */
27061 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27062
27063 /**
27064  * @class Roo.bootstrap.menu.Menu
27065  * @extends Roo.bootstrap.Component
27066  * Bootstrap Menu class - container for Menu
27067  * @cfg {String} html Text of the menu
27068  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27069  * @cfg {String} icon Font awesome icon
27070  * @cfg {String} pos Menu align to (top | bottom) default bottom
27071  * 
27072  * 
27073  * @constructor
27074  * Create a new Menu
27075  * @param {Object} config The config object
27076  */
27077
27078
27079 Roo.bootstrap.menu.Menu = function(config){
27080     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27081     
27082     this.addEvents({
27083         /**
27084          * @event beforeshow
27085          * Fires before this menu is displayed
27086          * @param {Roo.bootstrap.menu.Menu} this
27087          */
27088         beforeshow : true,
27089         /**
27090          * @event beforehide
27091          * Fires before this menu is hidden
27092          * @param {Roo.bootstrap.menu.Menu} this
27093          */
27094         beforehide : true,
27095         /**
27096          * @event show
27097          * Fires after this menu is displayed
27098          * @param {Roo.bootstrap.menu.Menu} this
27099          */
27100         show : true,
27101         /**
27102          * @event hide
27103          * Fires after this menu is hidden
27104          * @param {Roo.bootstrap.menu.Menu} this
27105          */
27106         hide : true,
27107         /**
27108          * @event click
27109          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27110          * @param {Roo.bootstrap.menu.Menu} this
27111          * @param {Roo.EventObject} e
27112          */
27113         click : true
27114     });
27115     
27116 };
27117
27118 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27119     
27120     submenu : false,
27121     html : '',
27122     weight : 'default',
27123     icon : false,
27124     pos : 'bottom',
27125     
27126     
27127     getChildContainer : function() {
27128         if(this.isSubMenu){
27129             return this.el;
27130         }
27131         
27132         return this.el.select('ul.dropdown-menu', true).first();  
27133     },
27134     
27135     getAutoCreate : function()
27136     {
27137         var text = [
27138             {
27139                 tag : 'span',
27140                 cls : 'roo-menu-text',
27141                 html : this.html
27142             }
27143         ];
27144         
27145         if(this.icon){
27146             text.unshift({
27147                 tag : 'i',
27148                 cls : 'fa ' + this.icon
27149             })
27150         }
27151         
27152         
27153         var cfg = {
27154             tag : 'div',
27155             cls : 'btn-group',
27156             cn : [
27157                 {
27158                     tag : 'button',
27159                     cls : 'dropdown-button btn btn-' + this.weight,
27160                     cn : text
27161                 },
27162                 {
27163                     tag : 'button',
27164                     cls : 'dropdown-toggle btn btn-' + this.weight,
27165                     cn : [
27166                         {
27167                             tag : 'span',
27168                             cls : 'caret'
27169                         }
27170                     ]
27171                 },
27172                 {
27173                     tag : 'ul',
27174                     cls : 'dropdown-menu'
27175                 }
27176             ]
27177             
27178         };
27179         
27180         if(this.pos == 'top'){
27181             cfg.cls += ' dropup';
27182         }
27183         
27184         if(this.isSubMenu){
27185             cfg = {
27186                 tag : 'ul',
27187                 cls : 'dropdown-menu'
27188             }
27189         }
27190         
27191         return cfg;
27192     },
27193     
27194     onRender : function(ct, position)
27195     {
27196         this.isSubMenu = ct.hasClass('dropdown-submenu');
27197         
27198         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27199     },
27200     
27201     initEvents : function() 
27202     {
27203         if(this.isSubMenu){
27204             return;
27205         }
27206         
27207         this.hidden = true;
27208         
27209         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27210         this.triggerEl.on('click', this.onTriggerPress, this);
27211         
27212         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27213         this.buttonEl.on('click', this.onClick, this);
27214         
27215     },
27216     
27217     list : function()
27218     {
27219         if(this.isSubMenu){
27220             return this.el;
27221         }
27222         
27223         return this.el.select('ul.dropdown-menu', true).first();
27224     },
27225     
27226     onClick : function(e)
27227     {
27228         this.fireEvent("click", this, e);
27229     },
27230     
27231     onTriggerPress  : function(e)
27232     {   
27233         if (this.isVisible()) {
27234             this.hide();
27235         } else {
27236             this.show();
27237         }
27238     },
27239     
27240     isVisible : function(){
27241         return !this.hidden;
27242     },
27243     
27244     show : function()
27245     {
27246         this.fireEvent("beforeshow", this);
27247         
27248         this.hidden = false;
27249         this.el.addClass('open');
27250         
27251         Roo.get(document).on("mouseup", this.onMouseUp, this);
27252         
27253         this.fireEvent("show", this);
27254         
27255         
27256     },
27257     
27258     hide : function()
27259     {
27260         this.fireEvent("beforehide", this);
27261         
27262         this.hidden = true;
27263         this.el.removeClass('open');
27264         
27265         Roo.get(document).un("mouseup", this.onMouseUp);
27266         
27267         this.fireEvent("hide", this);
27268     },
27269     
27270     onMouseUp : function()
27271     {
27272         this.hide();
27273     }
27274     
27275 });
27276
27277  
27278  /*
27279  * - LGPL
27280  *
27281  * menu item
27282  * 
27283  */
27284 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27285
27286 /**
27287  * @class Roo.bootstrap.menu.Item
27288  * @extends Roo.bootstrap.Component
27289  * Bootstrap MenuItem class
27290  * @cfg {Boolean} submenu (true | false) default false
27291  * @cfg {String} html text of the item
27292  * @cfg {String} href the link
27293  * @cfg {Boolean} disable (true | false) default false
27294  * @cfg {Boolean} preventDefault (true | false) default true
27295  * @cfg {String} icon Font awesome icon
27296  * @cfg {String} pos Submenu align to (left | right) default right 
27297  * 
27298  * 
27299  * @constructor
27300  * Create a new Item
27301  * @param {Object} config The config object
27302  */
27303
27304
27305 Roo.bootstrap.menu.Item = function(config){
27306     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27307     this.addEvents({
27308         /**
27309          * @event mouseover
27310          * Fires when the mouse is hovering over this menu
27311          * @param {Roo.bootstrap.menu.Item} this
27312          * @param {Roo.EventObject} e
27313          */
27314         mouseover : true,
27315         /**
27316          * @event mouseout
27317          * Fires when the mouse exits this menu
27318          * @param {Roo.bootstrap.menu.Item} this
27319          * @param {Roo.EventObject} e
27320          */
27321         mouseout : true,
27322         // raw events
27323         /**
27324          * @event click
27325          * The raw click event for the entire grid.
27326          * @param {Roo.EventObject} e
27327          */
27328         click : true
27329     });
27330 };
27331
27332 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27333     
27334     submenu : false,
27335     href : '',
27336     html : '',
27337     preventDefault: true,
27338     disable : false,
27339     icon : false,
27340     pos : 'right',
27341     
27342     getAutoCreate : function()
27343     {
27344         var text = [
27345             {
27346                 tag : 'span',
27347                 cls : 'roo-menu-item-text',
27348                 html : this.html
27349             }
27350         ];
27351         
27352         if(this.icon){
27353             text.unshift({
27354                 tag : 'i',
27355                 cls : 'fa ' + this.icon
27356             })
27357         }
27358         
27359         var cfg = {
27360             tag : 'li',
27361             cn : [
27362                 {
27363                     tag : 'a',
27364                     href : this.href || '#',
27365                     cn : text
27366                 }
27367             ]
27368         };
27369         
27370         if(this.disable){
27371             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27372         }
27373         
27374         if(this.submenu){
27375             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27376             
27377             if(this.pos == 'left'){
27378                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27379             }
27380         }
27381         
27382         return cfg;
27383     },
27384     
27385     initEvents : function() 
27386     {
27387         this.el.on('mouseover', this.onMouseOver, this);
27388         this.el.on('mouseout', this.onMouseOut, this);
27389         
27390         this.el.select('a', true).first().on('click', this.onClick, this);
27391         
27392     },
27393     
27394     onClick : function(e)
27395     {
27396         if(this.preventDefault){
27397             e.preventDefault();
27398         }
27399         
27400         this.fireEvent("click", this, e);
27401     },
27402     
27403     onMouseOver : function(e)
27404     {
27405         if(this.submenu && this.pos == 'left'){
27406             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27407         }
27408         
27409         this.fireEvent("mouseover", this, e);
27410     },
27411     
27412     onMouseOut : function(e)
27413     {
27414         this.fireEvent("mouseout", this, e);
27415     }
27416 });
27417
27418  
27419
27420  /*
27421  * - LGPL
27422  *
27423  * menu separator
27424  * 
27425  */
27426 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27427
27428 /**
27429  * @class Roo.bootstrap.menu.Separator
27430  * @extends Roo.bootstrap.Component
27431  * Bootstrap Separator class
27432  * 
27433  * @constructor
27434  * Create a new Separator
27435  * @param {Object} config The config object
27436  */
27437
27438
27439 Roo.bootstrap.menu.Separator = function(config){
27440     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27441 };
27442
27443 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27444     
27445     getAutoCreate : function(){
27446         var cfg = {
27447             tag : 'li',
27448             cls: 'divider'
27449         };
27450         
27451         return cfg;
27452     }
27453    
27454 });
27455
27456  
27457
27458  /*
27459  * - LGPL
27460  *
27461  * Tooltip
27462  * 
27463  */
27464
27465 /**
27466  * @class Roo.bootstrap.Tooltip
27467  * Bootstrap Tooltip class
27468  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27469  * to determine which dom element triggers the tooltip.
27470  * 
27471  * It needs to add support for additional attributes like tooltip-position
27472  * 
27473  * @constructor
27474  * Create a new Toolti
27475  * @param {Object} config The config object
27476  */
27477
27478 Roo.bootstrap.Tooltip = function(config){
27479     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27480     
27481     this.alignment = Roo.bootstrap.Tooltip.alignment;
27482     
27483     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27484         this.alignment = config.alignment;
27485     }
27486     
27487 };
27488
27489 Roo.apply(Roo.bootstrap.Tooltip, {
27490     /**
27491      * @function init initialize tooltip monitoring.
27492      * @static
27493      */
27494     currentEl : false,
27495     currentTip : false,
27496     currentRegion : false,
27497     
27498     //  init : delay?
27499     
27500     init : function()
27501     {
27502         Roo.get(document).on('mouseover', this.enter ,this);
27503         Roo.get(document).on('mouseout', this.leave, this);
27504          
27505         
27506         this.currentTip = new Roo.bootstrap.Tooltip();
27507     },
27508     
27509     enter : function(ev)
27510     {
27511         var dom = ev.getTarget();
27512         
27513         //Roo.log(['enter',dom]);
27514         var el = Roo.fly(dom);
27515         if (this.currentEl) {
27516             //Roo.log(dom);
27517             //Roo.log(this.currentEl);
27518             //Roo.log(this.currentEl.contains(dom));
27519             if (this.currentEl == el) {
27520                 return;
27521             }
27522             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27523                 return;
27524             }
27525
27526         }
27527         
27528         if (this.currentTip.el) {
27529             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27530         }    
27531         //Roo.log(ev);
27532         
27533         if(!el || el.dom == document){
27534             return;
27535         }
27536         
27537         var bindEl = el;
27538         
27539         // you can not look for children, as if el is the body.. then everythign is the child..
27540         if (!el.attr('tooltip')) { //
27541             if (!el.select("[tooltip]").elements.length) {
27542                 return;
27543             }
27544             // is the mouse over this child...?
27545             bindEl = el.select("[tooltip]").first();
27546             var xy = ev.getXY();
27547             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27548                 //Roo.log("not in region.");
27549                 return;
27550             }
27551             //Roo.log("child element over..");
27552             
27553         }
27554         this.currentEl = bindEl;
27555         this.currentTip.bind(bindEl);
27556         this.currentRegion = Roo.lib.Region.getRegion(dom);
27557         this.currentTip.enter();
27558         
27559     },
27560     leave : function(ev)
27561     {
27562         var dom = ev.getTarget();
27563         //Roo.log(['leave',dom]);
27564         if (!this.currentEl) {
27565             return;
27566         }
27567         
27568         
27569         if (dom != this.currentEl.dom) {
27570             return;
27571         }
27572         var xy = ev.getXY();
27573         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27574             return;
27575         }
27576         // only activate leave if mouse cursor is outside... bounding box..
27577         
27578         
27579         
27580         
27581         if (this.currentTip) {
27582             this.currentTip.leave();
27583         }
27584         //Roo.log('clear currentEl');
27585         this.currentEl = false;
27586         
27587         
27588     },
27589     alignment : {
27590         'left' : ['r-l', [-2,0], 'right'],
27591         'right' : ['l-r', [2,0], 'left'],
27592         'bottom' : ['t-b', [0,2], 'top'],
27593         'top' : [ 'b-t', [0,-2], 'bottom']
27594     }
27595     
27596 });
27597
27598
27599 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27600     
27601     
27602     bindEl : false,
27603     
27604     delay : null, // can be { show : 300 , hide: 500}
27605     
27606     timeout : null,
27607     
27608     hoverState : null, //???
27609     
27610     placement : 'bottom', 
27611     
27612     alignment : false,
27613     
27614     getAutoCreate : function(){
27615     
27616         var cfg = {
27617            cls : 'tooltip',
27618            role : 'tooltip',
27619            cn : [
27620                 {
27621                     cls : 'tooltip-arrow'
27622                 },
27623                 {
27624                     cls : 'tooltip-inner'
27625                 }
27626            ]
27627         };
27628         
27629         return cfg;
27630     },
27631     bind : function(el)
27632     {
27633         this.bindEl = el;
27634     },
27635       
27636     
27637     enter : function () {
27638        
27639         if (this.timeout != null) {
27640             clearTimeout(this.timeout);
27641         }
27642         
27643         this.hoverState = 'in';
27644          //Roo.log("enter - show");
27645         if (!this.delay || !this.delay.show) {
27646             this.show();
27647             return;
27648         }
27649         var _t = this;
27650         this.timeout = setTimeout(function () {
27651             if (_t.hoverState == 'in') {
27652                 _t.show();
27653             }
27654         }, this.delay.show);
27655     },
27656     leave : function()
27657     {
27658         clearTimeout(this.timeout);
27659     
27660         this.hoverState = 'out';
27661          if (!this.delay || !this.delay.hide) {
27662             this.hide();
27663             return;
27664         }
27665        
27666         var _t = this;
27667         this.timeout = setTimeout(function () {
27668             //Roo.log("leave - timeout");
27669             
27670             if (_t.hoverState == 'out') {
27671                 _t.hide();
27672                 Roo.bootstrap.Tooltip.currentEl = false;
27673             }
27674         }, delay);
27675     },
27676     
27677     show : function (msg)
27678     {
27679         if (!this.el) {
27680             this.render(document.body);
27681         }
27682         // set content.
27683         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27684         
27685         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27686         
27687         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27688         
27689         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27690         
27691         var placement = typeof this.placement == 'function' ?
27692             this.placement.call(this, this.el, on_el) :
27693             this.placement;
27694             
27695         var autoToken = /\s?auto?\s?/i;
27696         var autoPlace = autoToken.test(placement);
27697         if (autoPlace) {
27698             placement = placement.replace(autoToken, '') || 'top';
27699         }
27700         
27701         //this.el.detach()
27702         //this.el.setXY([0,0]);
27703         this.el.show();
27704         //this.el.dom.style.display='block';
27705         
27706         //this.el.appendTo(on_el);
27707         
27708         var p = this.getPosition();
27709         var box = this.el.getBox();
27710         
27711         if (autoPlace) {
27712             // fixme..
27713         }
27714         
27715         var align = this.alignment[placement];
27716         
27717         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27718         
27719         if(placement == 'top' || placement == 'bottom'){
27720             if(xy[0] < 0){
27721                 placement = 'right';
27722             }
27723             
27724             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27725                 placement = 'left';
27726             }
27727             
27728             var scroll = Roo.select('body', true).first().getScroll();
27729             
27730             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27731                 placement = 'top';
27732             }
27733             
27734             align = this.alignment[placement];
27735         }
27736         
27737         this.el.alignTo(this.bindEl, align[0],align[1]);
27738         //var arrow = this.el.select('.arrow',true).first();
27739         //arrow.set(align[2], 
27740         
27741         this.el.addClass(placement);
27742         
27743         this.el.addClass('in fade');
27744         
27745         this.hoverState = null;
27746         
27747         if (this.el.hasClass('fade')) {
27748             // fade it?
27749         }
27750         
27751     },
27752     hide : function()
27753     {
27754          
27755         if (!this.el) {
27756             return;
27757         }
27758         //this.el.setXY([0,0]);
27759         this.el.removeClass('in');
27760         //this.el.hide();
27761         
27762     }
27763     
27764 });
27765  
27766
27767  /*
27768  * - LGPL
27769  *
27770  * Location Picker
27771  * 
27772  */
27773
27774 /**
27775  * @class Roo.bootstrap.LocationPicker
27776  * @extends Roo.bootstrap.Component
27777  * Bootstrap LocationPicker class
27778  * @cfg {Number} latitude Position when init default 0
27779  * @cfg {Number} longitude Position when init default 0
27780  * @cfg {Number} zoom default 15
27781  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27782  * @cfg {Boolean} mapTypeControl default false
27783  * @cfg {Boolean} disableDoubleClickZoom default false
27784  * @cfg {Boolean} scrollwheel default true
27785  * @cfg {Boolean} streetViewControl default false
27786  * @cfg {Number} radius default 0
27787  * @cfg {String} locationName
27788  * @cfg {Boolean} draggable default true
27789  * @cfg {Boolean} enableAutocomplete default false
27790  * @cfg {Boolean} enableReverseGeocode default true
27791  * @cfg {String} markerTitle
27792  * 
27793  * @constructor
27794  * Create a new LocationPicker
27795  * @param {Object} config The config object
27796  */
27797
27798
27799 Roo.bootstrap.LocationPicker = function(config){
27800     
27801     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27802     
27803     this.addEvents({
27804         /**
27805          * @event initial
27806          * Fires when the picker initialized.
27807          * @param {Roo.bootstrap.LocationPicker} this
27808          * @param {Google Location} location
27809          */
27810         initial : true,
27811         /**
27812          * @event positionchanged
27813          * Fires when the picker position changed.
27814          * @param {Roo.bootstrap.LocationPicker} this
27815          * @param {Google Location} location
27816          */
27817         positionchanged : true,
27818         /**
27819          * @event resize
27820          * Fires when the map resize.
27821          * @param {Roo.bootstrap.LocationPicker} this
27822          */
27823         resize : true,
27824         /**
27825          * @event show
27826          * Fires when the map show.
27827          * @param {Roo.bootstrap.LocationPicker} this
27828          */
27829         show : true,
27830         /**
27831          * @event hide
27832          * Fires when the map hide.
27833          * @param {Roo.bootstrap.LocationPicker} this
27834          */
27835         hide : true,
27836         /**
27837          * @event mapClick
27838          * Fires when click the map.
27839          * @param {Roo.bootstrap.LocationPicker} this
27840          * @param {Map event} e
27841          */
27842         mapClick : true,
27843         /**
27844          * @event mapRightClick
27845          * Fires when right click the map.
27846          * @param {Roo.bootstrap.LocationPicker} this
27847          * @param {Map event} e
27848          */
27849         mapRightClick : true,
27850         /**
27851          * @event markerClick
27852          * Fires when click the marker.
27853          * @param {Roo.bootstrap.LocationPicker} this
27854          * @param {Map event} e
27855          */
27856         markerClick : true,
27857         /**
27858          * @event markerRightClick
27859          * Fires when right click the marker.
27860          * @param {Roo.bootstrap.LocationPicker} this
27861          * @param {Map event} e
27862          */
27863         markerRightClick : true,
27864         /**
27865          * @event OverlayViewDraw
27866          * Fires when OverlayView Draw
27867          * @param {Roo.bootstrap.LocationPicker} this
27868          */
27869         OverlayViewDraw : true,
27870         /**
27871          * @event OverlayViewOnAdd
27872          * Fires when OverlayView Draw
27873          * @param {Roo.bootstrap.LocationPicker} this
27874          */
27875         OverlayViewOnAdd : true,
27876         /**
27877          * @event OverlayViewOnRemove
27878          * Fires when OverlayView Draw
27879          * @param {Roo.bootstrap.LocationPicker} this
27880          */
27881         OverlayViewOnRemove : true,
27882         /**
27883          * @event OverlayViewShow
27884          * Fires when OverlayView Draw
27885          * @param {Roo.bootstrap.LocationPicker} this
27886          * @param {Pixel} cpx
27887          */
27888         OverlayViewShow : true,
27889         /**
27890          * @event OverlayViewHide
27891          * Fires when OverlayView Draw
27892          * @param {Roo.bootstrap.LocationPicker} this
27893          */
27894         OverlayViewHide : true,
27895         /**
27896          * @event loadexception
27897          * Fires when load google lib failed.
27898          * @param {Roo.bootstrap.LocationPicker} this
27899          */
27900         loadexception : true
27901     });
27902         
27903 };
27904
27905 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27906     
27907     gMapContext: false,
27908     
27909     latitude: 0,
27910     longitude: 0,
27911     zoom: 15,
27912     mapTypeId: false,
27913     mapTypeControl: false,
27914     disableDoubleClickZoom: false,
27915     scrollwheel: true,
27916     streetViewControl: false,
27917     radius: 0,
27918     locationName: '',
27919     draggable: true,
27920     enableAutocomplete: false,
27921     enableReverseGeocode: true,
27922     markerTitle: '',
27923     
27924     getAutoCreate: function()
27925     {
27926
27927         var cfg = {
27928             tag: 'div',
27929             cls: 'roo-location-picker'
27930         };
27931         
27932         return cfg
27933     },
27934     
27935     initEvents: function(ct, position)
27936     {       
27937         if(!this.el.getWidth() || this.isApplied()){
27938             return;
27939         }
27940         
27941         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27942         
27943         this.initial();
27944     },
27945     
27946     initial: function()
27947     {
27948         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27949             this.fireEvent('loadexception', this);
27950             return;
27951         }
27952         
27953         if(!this.mapTypeId){
27954             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27955         }
27956         
27957         this.gMapContext = this.GMapContext();
27958         
27959         this.initOverlayView();
27960         
27961         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27962         
27963         var _this = this;
27964                 
27965         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27966             _this.setPosition(_this.gMapContext.marker.position);
27967         });
27968         
27969         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27970             _this.fireEvent('mapClick', this, event);
27971             
27972         });
27973
27974         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27975             _this.fireEvent('mapRightClick', this, event);
27976             
27977         });
27978         
27979         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27980             _this.fireEvent('markerClick', this, event);
27981             
27982         });
27983
27984         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27985             _this.fireEvent('markerRightClick', this, event);
27986             
27987         });
27988         
27989         this.setPosition(this.gMapContext.location);
27990         
27991         this.fireEvent('initial', this, this.gMapContext.location);
27992     },
27993     
27994     initOverlayView: function()
27995     {
27996         var _this = this;
27997         
27998         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27999             
28000             draw: function()
28001             {
28002                 _this.fireEvent('OverlayViewDraw', _this);
28003             },
28004             
28005             onAdd: function()
28006             {
28007                 _this.fireEvent('OverlayViewOnAdd', _this);
28008             },
28009             
28010             onRemove: function()
28011             {
28012                 _this.fireEvent('OverlayViewOnRemove', _this);
28013             },
28014             
28015             show: function(cpx)
28016             {
28017                 _this.fireEvent('OverlayViewShow', _this, cpx);
28018             },
28019             
28020             hide: function()
28021             {
28022                 _this.fireEvent('OverlayViewHide', _this);
28023             }
28024             
28025         });
28026     },
28027     
28028     fromLatLngToContainerPixel: function(event)
28029     {
28030         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28031     },
28032     
28033     isApplied: function() 
28034     {
28035         return this.getGmapContext() == false ? false : true;
28036     },
28037     
28038     getGmapContext: function() 
28039     {
28040         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28041     },
28042     
28043     GMapContext: function() 
28044     {
28045         var position = new google.maps.LatLng(this.latitude, this.longitude);
28046         
28047         var _map = new google.maps.Map(this.el.dom, {
28048             center: position,
28049             zoom: this.zoom,
28050             mapTypeId: this.mapTypeId,
28051             mapTypeControl: this.mapTypeControl,
28052             disableDoubleClickZoom: this.disableDoubleClickZoom,
28053             scrollwheel: this.scrollwheel,
28054             streetViewControl: this.streetViewControl,
28055             locationName: this.locationName,
28056             draggable: this.draggable,
28057             enableAutocomplete: this.enableAutocomplete,
28058             enableReverseGeocode: this.enableReverseGeocode
28059         });
28060         
28061         var _marker = new google.maps.Marker({
28062             position: position,
28063             map: _map,
28064             title: this.markerTitle,
28065             draggable: this.draggable
28066         });
28067         
28068         return {
28069             map: _map,
28070             marker: _marker,
28071             circle: null,
28072             location: position,
28073             radius: this.radius,
28074             locationName: this.locationName,
28075             addressComponents: {
28076                 formatted_address: null,
28077                 addressLine1: null,
28078                 addressLine2: null,
28079                 streetName: null,
28080                 streetNumber: null,
28081                 city: null,
28082                 district: null,
28083                 state: null,
28084                 stateOrProvince: null
28085             },
28086             settings: this,
28087             domContainer: this.el.dom,
28088             geodecoder: new google.maps.Geocoder()
28089         };
28090     },
28091     
28092     drawCircle: function(center, radius, options) 
28093     {
28094         if (this.gMapContext.circle != null) {
28095             this.gMapContext.circle.setMap(null);
28096         }
28097         if (radius > 0) {
28098             radius *= 1;
28099             options = Roo.apply({}, options, {
28100                 strokeColor: "#0000FF",
28101                 strokeOpacity: .35,
28102                 strokeWeight: 2,
28103                 fillColor: "#0000FF",
28104                 fillOpacity: .2
28105             });
28106             
28107             options.map = this.gMapContext.map;
28108             options.radius = radius;
28109             options.center = center;
28110             this.gMapContext.circle = new google.maps.Circle(options);
28111             return this.gMapContext.circle;
28112         }
28113         
28114         return null;
28115     },
28116     
28117     setPosition: function(location) 
28118     {
28119         this.gMapContext.location = location;
28120         this.gMapContext.marker.setPosition(location);
28121         this.gMapContext.map.panTo(location);
28122         this.drawCircle(location, this.gMapContext.radius, {});
28123         
28124         var _this = this;
28125         
28126         if (this.gMapContext.settings.enableReverseGeocode) {
28127             this.gMapContext.geodecoder.geocode({
28128                 latLng: this.gMapContext.location
28129             }, function(results, status) {
28130                 
28131                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28132                     _this.gMapContext.locationName = results[0].formatted_address;
28133                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28134                     
28135                     _this.fireEvent('positionchanged', this, location);
28136                 }
28137             });
28138             
28139             return;
28140         }
28141         
28142         this.fireEvent('positionchanged', this, location);
28143     },
28144     
28145     resize: function()
28146     {
28147         google.maps.event.trigger(this.gMapContext.map, "resize");
28148         
28149         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28150         
28151         this.fireEvent('resize', this);
28152     },
28153     
28154     setPositionByLatLng: function(latitude, longitude)
28155     {
28156         this.setPosition(new google.maps.LatLng(latitude, longitude));
28157     },
28158     
28159     getCurrentPosition: function() 
28160     {
28161         return {
28162             latitude: this.gMapContext.location.lat(),
28163             longitude: this.gMapContext.location.lng()
28164         };
28165     },
28166     
28167     getAddressName: function() 
28168     {
28169         return this.gMapContext.locationName;
28170     },
28171     
28172     getAddressComponents: function() 
28173     {
28174         return this.gMapContext.addressComponents;
28175     },
28176     
28177     address_component_from_google_geocode: function(address_components) 
28178     {
28179         var result = {};
28180         
28181         for (var i = 0; i < address_components.length; i++) {
28182             var component = address_components[i];
28183             if (component.types.indexOf("postal_code") >= 0) {
28184                 result.postalCode = component.short_name;
28185             } else if (component.types.indexOf("street_number") >= 0) {
28186                 result.streetNumber = component.short_name;
28187             } else if (component.types.indexOf("route") >= 0) {
28188                 result.streetName = component.short_name;
28189             } else if (component.types.indexOf("neighborhood") >= 0) {
28190                 result.city = component.short_name;
28191             } else if (component.types.indexOf("locality") >= 0) {
28192                 result.city = component.short_name;
28193             } else if (component.types.indexOf("sublocality") >= 0) {
28194                 result.district = component.short_name;
28195             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28196                 result.stateOrProvince = component.short_name;
28197             } else if (component.types.indexOf("country") >= 0) {
28198                 result.country = component.short_name;
28199             }
28200         }
28201         
28202         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28203         result.addressLine2 = "";
28204         return result;
28205     },
28206     
28207     setZoomLevel: function(zoom)
28208     {
28209         this.gMapContext.map.setZoom(zoom);
28210     },
28211     
28212     show: function()
28213     {
28214         if(!this.el){
28215             return;
28216         }
28217         
28218         this.el.show();
28219         
28220         this.resize();
28221         
28222         this.fireEvent('show', this);
28223     },
28224     
28225     hide: function()
28226     {
28227         if(!this.el){
28228             return;
28229         }
28230         
28231         this.el.hide();
28232         
28233         this.fireEvent('hide', this);
28234     }
28235     
28236 });
28237
28238 Roo.apply(Roo.bootstrap.LocationPicker, {
28239     
28240     OverlayView : function(map, options)
28241     {
28242         options = options || {};
28243         
28244         this.setMap(map);
28245     }
28246     
28247     
28248 });/**
28249  * @class Roo.bootstrap.Alert
28250  * @extends Roo.bootstrap.Component
28251  * Bootstrap Alert class - shows an alert area box
28252  * eg
28253  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28254   Enter a valid email address
28255 </div>
28256  * @licence LGPL
28257  * @cfg {String} title The title of alert
28258  * @cfg {String} html The content of alert
28259  * @cfg {String} weight (  success | info | warning | danger )
28260  * @cfg {String} faicon font-awesomeicon
28261  * 
28262  * @constructor
28263  * Create a new alert
28264  * @param {Object} config The config object
28265  */
28266
28267
28268 Roo.bootstrap.Alert = function(config){
28269     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28270     
28271 };
28272
28273 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28274     
28275     title: '',
28276     html: '',
28277     weight: false,
28278     faicon: false,
28279     
28280     getAutoCreate : function()
28281     {
28282         
28283         var cfg = {
28284             tag : 'div',
28285             cls : 'alert',
28286             cn : [
28287                 {
28288                     tag : 'i',
28289                     cls : 'roo-alert-icon'
28290                     
28291                 },
28292                 {
28293                     tag : 'b',
28294                     cls : 'roo-alert-title',
28295                     html : this.title
28296                 },
28297                 {
28298                     tag : 'span',
28299                     cls : 'roo-alert-text',
28300                     html : this.html
28301                 }
28302             ]
28303         };
28304         
28305         if(this.faicon){
28306             cfg.cn[0].cls += ' fa ' + this.faicon;
28307         }
28308         
28309         if(this.weight){
28310             cfg.cls += ' alert-' + this.weight;
28311         }
28312         
28313         return cfg;
28314     },
28315     
28316     initEvents: function() 
28317     {
28318         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28319     },
28320     
28321     setTitle : function(str)
28322     {
28323         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28324     },
28325     
28326     setText : function(str)
28327     {
28328         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28329     },
28330     
28331     setWeight : function(weight)
28332     {
28333         if(this.weight){
28334             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28335         }
28336         
28337         this.weight = weight;
28338         
28339         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28340     },
28341     
28342     setIcon : function(icon)
28343     {
28344         if(this.faicon){
28345             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28346         }
28347         
28348         this.faicon = icon;
28349         
28350         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28351     },
28352     
28353     hide: function() 
28354     {
28355         this.el.hide();   
28356     },
28357     
28358     show: function() 
28359     {  
28360         this.el.show();   
28361     }
28362     
28363 });
28364
28365  
28366 /*
28367 * Licence: LGPL
28368 */
28369
28370 /**
28371  * @class Roo.bootstrap.UploadCropbox
28372  * @extends Roo.bootstrap.Component
28373  * Bootstrap UploadCropbox class
28374  * @cfg {String} emptyText show when image has been loaded
28375  * @cfg {String} rotateNotify show when image too small to rotate
28376  * @cfg {Number} errorTimeout default 3000
28377  * @cfg {Number} minWidth default 300
28378  * @cfg {Number} minHeight default 300
28379  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28380  * @cfg {Boolean} isDocument (true|false) default false
28381  * @cfg {String} url action url
28382  * @cfg {String} paramName default 'imageUpload'
28383  * @cfg {String} method default POST
28384  * @cfg {Boolean} loadMask (true|false) default true
28385  * @cfg {Boolean} loadingText default 'Loading...'
28386  * 
28387  * @constructor
28388  * Create a new UploadCropbox
28389  * @param {Object} config The config object
28390  */
28391
28392 Roo.bootstrap.UploadCropbox = function(config){
28393     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28394     
28395     this.addEvents({
28396         /**
28397          * @event beforeselectfile
28398          * Fire before select file
28399          * @param {Roo.bootstrap.UploadCropbox} this
28400          */
28401         "beforeselectfile" : true,
28402         /**
28403          * @event initial
28404          * Fire after initEvent
28405          * @param {Roo.bootstrap.UploadCropbox} this
28406          */
28407         "initial" : true,
28408         /**
28409          * @event crop
28410          * Fire after initEvent
28411          * @param {Roo.bootstrap.UploadCropbox} this
28412          * @param {String} data
28413          */
28414         "crop" : true,
28415         /**
28416          * @event prepare
28417          * Fire when preparing the file data
28418          * @param {Roo.bootstrap.UploadCropbox} this
28419          * @param {Object} file
28420          */
28421         "prepare" : true,
28422         /**
28423          * @event exception
28424          * Fire when get exception
28425          * @param {Roo.bootstrap.UploadCropbox} this
28426          * @param {XMLHttpRequest} xhr
28427          */
28428         "exception" : true,
28429         /**
28430          * @event beforeloadcanvas
28431          * Fire before load the canvas
28432          * @param {Roo.bootstrap.UploadCropbox} this
28433          * @param {String} src
28434          */
28435         "beforeloadcanvas" : true,
28436         /**
28437          * @event trash
28438          * Fire when trash image
28439          * @param {Roo.bootstrap.UploadCropbox} this
28440          */
28441         "trash" : true,
28442         /**
28443          * @event download
28444          * Fire when download the image
28445          * @param {Roo.bootstrap.UploadCropbox} this
28446          */
28447         "download" : true,
28448         /**
28449          * @event footerbuttonclick
28450          * Fire when footerbuttonclick
28451          * @param {Roo.bootstrap.UploadCropbox} this
28452          * @param {String} type
28453          */
28454         "footerbuttonclick" : true,
28455         /**
28456          * @event resize
28457          * Fire when resize
28458          * @param {Roo.bootstrap.UploadCropbox} this
28459          */
28460         "resize" : true,
28461         /**
28462          * @event rotate
28463          * Fire when rotate the image
28464          * @param {Roo.bootstrap.UploadCropbox} this
28465          * @param {String} pos
28466          */
28467         "rotate" : true,
28468         /**
28469          * @event inspect
28470          * Fire when inspect the file
28471          * @param {Roo.bootstrap.UploadCropbox} this
28472          * @param {Object} file
28473          */
28474         "inspect" : true,
28475         /**
28476          * @event upload
28477          * Fire when xhr upload the file
28478          * @param {Roo.bootstrap.UploadCropbox} this
28479          * @param {Object} data
28480          */
28481         "upload" : true,
28482         /**
28483          * @event arrange
28484          * Fire when arrange the file data
28485          * @param {Roo.bootstrap.UploadCropbox} this
28486          * @param {Object} formData
28487          */
28488         "arrange" : true
28489     });
28490     
28491     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28492 };
28493
28494 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28495     
28496     emptyText : 'Click to upload image',
28497     rotateNotify : 'Image is too small to rotate',
28498     errorTimeout : 3000,
28499     scale : 0,
28500     baseScale : 1,
28501     rotate : 0,
28502     dragable : false,
28503     pinching : false,
28504     mouseX : 0,
28505     mouseY : 0,
28506     cropData : false,
28507     minWidth : 300,
28508     minHeight : 300,
28509     file : false,
28510     exif : {},
28511     baseRotate : 1,
28512     cropType : 'image/jpeg',
28513     buttons : false,
28514     canvasLoaded : false,
28515     isDocument : false,
28516     method : 'POST',
28517     paramName : 'imageUpload',
28518     loadMask : true,
28519     loadingText : 'Loading...',
28520     maskEl : false,
28521     
28522     getAutoCreate : function()
28523     {
28524         var cfg = {
28525             tag : 'div',
28526             cls : 'roo-upload-cropbox',
28527             cn : [
28528                 {
28529                     tag : 'input',
28530                     cls : 'roo-upload-cropbox-selector',
28531                     type : 'file'
28532                 },
28533                 {
28534                     tag : 'div',
28535                     cls : 'roo-upload-cropbox-body',
28536                     style : 'cursor:pointer',
28537                     cn : [
28538                         {
28539                             tag : 'div',
28540                             cls : 'roo-upload-cropbox-preview'
28541                         },
28542                         {
28543                             tag : 'div',
28544                             cls : 'roo-upload-cropbox-thumb'
28545                         },
28546                         {
28547                             tag : 'div',
28548                             cls : 'roo-upload-cropbox-empty-notify',
28549                             html : this.emptyText
28550                         },
28551                         {
28552                             tag : 'div',
28553                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28554                             html : this.rotateNotify
28555                         }
28556                     ]
28557                 },
28558                 {
28559                     tag : 'div',
28560                     cls : 'roo-upload-cropbox-footer',
28561                     cn : {
28562                         tag : 'div',
28563                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28564                         cn : []
28565                     }
28566                 }
28567             ]
28568         };
28569         
28570         return cfg;
28571     },
28572     
28573     onRender : function(ct, position)
28574     {
28575         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28576         
28577         if (this.buttons.length) {
28578             
28579             Roo.each(this.buttons, function(bb) {
28580                 
28581                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28582                 
28583                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28584                 
28585             }, this);
28586         }
28587         
28588         if(this.loadMask){
28589             this.maskEl = this.el;
28590         }
28591     },
28592     
28593     initEvents : function()
28594     {
28595         this.urlAPI = (window.createObjectURL && window) || 
28596                                 (window.URL && URL.revokeObjectURL && URL) || 
28597                                 (window.webkitURL && webkitURL);
28598                         
28599         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28600         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28601         
28602         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28603         this.selectorEl.hide();
28604         
28605         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28606         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28607         
28608         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28609         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28610         this.thumbEl.hide();
28611         
28612         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28613         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28614         
28615         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28616         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28617         this.errorEl.hide();
28618         
28619         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28620         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28621         this.footerEl.hide();
28622         
28623         this.setThumbBoxSize();
28624         
28625         this.bind();
28626         
28627         this.resize();
28628         
28629         this.fireEvent('initial', this);
28630     },
28631
28632     bind : function()
28633     {
28634         var _this = this;
28635         
28636         window.addEventListener("resize", function() { _this.resize(); } );
28637         
28638         this.bodyEl.on('click', this.beforeSelectFile, this);
28639         
28640         if(Roo.isTouch){
28641             this.bodyEl.on('touchstart', this.onTouchStart, this);
28642             this.bodyEl.on('touchmove', this.onTouchMove, this);
28643             this.bodyEl.on('touchend', this.onTouchEnd, this);
28644         }
28645         
28646         if(!Roo.isTouch){
28647             this.bodyEl.on('mousedown', this.onMouseDown, this);
28648             this.bodyEl.on('mousemove', this.onMouseMove, this);
28649             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28650             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28651             Roo.get(document).on('mouseup', this.onMouseUp, this);
28652         }
28653         
28654         this.selectorEl.on('change', this.onFileSelected, this);
28655     },
28656     
28657     reset : function()
28658     {    
28659         this.scale = 0;
28660         this.baseScale = 1;
28661         this.rotate = 0;
28662         this.baseRotate = 1;
28663         this.dragable = false;
28664         this.pinching = false;
28665         this.mouseX = 0;
28666         this.mouseY = 0;
28667         this.cropData = false;
28668         this.notifyEl.dom.innerHTML = this.emptyText;
28669         
28670         this.selectorEl.dom.value = '';
28671         
28672     },
28673     
28674     resize : function()
28675     {
28676         if(this.fireEvent('resize', this) != false){
28677             this.setThumbBoxPosition();
28678             this.setCanvasPosition();
28679         }
28680     },
28681     
28682     onFooterButtonClick : function(e, el, o, type)
28683     {
28684         switch (type) {
28685             case 'rotate-left' :
28686                 this.onRotateLeft(e);
28687                 break;
28688             case 'rotate-right' :
28689                 this.onRotateRight(e);
28690                 break;
28691             case 'picture' :
28692                 this.beforeSelectFile(e);
28693                 break;
28694             case 'trash' :
28695                 this.trash(e);
28696                 break;
28697             case 'crop' :
28698                 this.crop(e);
28699                 break;
28700             case 'download' :
28701                 this.download(e);
28702                 break;
28703             default :
28704                 break;
28705         }
28706         
28707         this.fireEvent('footerbuttonclick', this, type);
28708     },
28709     
28710     beforeSelectFile : function(e)
28711     {
28712         e.preventDefault();
28713         
28714         if(this.fireEvent('beforeselectfile', this) != false){
28715             this.selectorEl.dom.click();
28716         }
28717     },
28718     
28719     onFileSelected : function(e)
28720     {
28721         e.preventDefault();
28722         
28723         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28724             return;
28725         }
28726         
28727         var file = this.selectorEl.dom.files[0];
28728         
28729         if(this.fireEvent('inspect', this, file) != false){
28730             this.prepare(file);
28731         }
28732         
28733     },
28734     
28735     trash : function(e)
28736     {
28737         this.fireEvent('trash', this);
28738     },
28739     
28740     download : function(e)
28741     {
28742         this.fireEvent('download', this);
28743     },
28744     
28745     loadCanvas : function(src)
28746     {   
28747         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28748             
28749             this.reset();
28750             
28751             this.imageEl = document.createElement('img');
28752             
28753             var _this = this;
28754             
28755             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28756             
28757             this.imageEl.src = src;
28758         }
28759     },
28760     
28761     onLoadCanvas : function()
28762     {   
28763         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28764         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28765         
28766         this.bodyEl.un('click', this.beforeSelectFile, this);
28767         
28768         this.notifyEl.hide();
28769         this.thumbEl.show();
28770         this.footerEl.show();
28771         
28772         this.baseRotateLevel();
28773         
28774         if(this.isDocument){
28775             this.setThumbBoxSize();
28776         }
28777         
28778         this.setThumbBoxPosition();
28779         
28780         this.baseScaleLevel();
28781         
28782         this.draw();
28783         
28784         this.resize();
28785         
28786         this.canvasLoaded = true;
28787         
28788         if(this.loadMask){
28789             this.maskEl.unmask();
28790         }
28791         
28792     },
28793     
28794     setCanvasPosition : function()
28795     {   
28796         if(!this.canvasEl){
28797             return;
28798         }
28799         
28800         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28801         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28802         
28803         this.previewEl.setLeft(pw);
28804         this.previewEl.setTop(ph);
28805         
28806     },
28807     
28808     onMouseDown : function(e)
28809     {   
28810         e.stopEvent();
28811         
28812         this.dragable = true;
28813         this.pinching = false;
28814         
28815         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28816             this.dragable = false;
28817             return;
28818         }
28819         
28820         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28821         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28822         
28823     },
28824     
28825     onMouseMove : function(e)
28826     {   
28827         e.stopEvent();
28828         
28829         if(!this.canvasLoaded){
28830             return;
28831         }
28832         
28833         if (!this.dragable){
28834             return;
28835         }
28836         
28837         var minX = Math.ceil(this.thumbEl.getLeft(true));
28838         var minY = Math.ceil(this.thumbEl.getTop(true));
28839         
28840         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28841         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28842         
28843         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28844         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28845         
28846         x = x - this.mouseX;
28847         y = y - this.mouseY;
28848         
28849         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28850         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28851         
28852         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28853         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28854         
28855         this.previewEl.setLeft(bgX);
28856         this.previewEl.setTop(bgY);
28857         
28858         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28859         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28860     },
28861     
28862     onMouseUp : function(e)
28863     {   
28864         e.stopEvent();
28865         
28866         this.dragable = false;
28867     },
28868     
28869     onMouseWheel : function(e)
28870     {   
28871         e.stopEvent();
28872         
28873         this.startScale = this.scale;
28874         
28875         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28876         
28877         if(!this.zoomable()){
28878             this.scale = this.startScale;
28879             return;
28880         }
28881         
28882         this.draw();
28883         
28884         return;
28885     },
28886     
28887     zoomable : function()
28888     {
28889         var minScale = this.thumbEl.getWidth() / this.minWidth;
28890         
28891         if(this.minWidth < this.minHeight){
28892             minScale = this.thumbEl.getHeight() / this.minHeight;
28893         }
28894         
28895         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28896         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28897         
28898         if(
28899                 this.isDocument &&
28900                 (this.rotate == 0 || this.rotate == 180) && 
28901                 (
28902                     width > this.imageEl.OriginWidth || 
28903                     height > this.imageEl.OriginHeight ||
28904                     (width < this.minWidth && height < this.minHeight)
28905                 )
28906         ){
28907             return false;
28908         }
28909         
28910         if(
28911                 this.isDocument &&
28912                 (this.rotate == 90 || this.rotate == 270) && 
28913                 (
28914                     width > this.imageEl.OriginWidth || 
28915                     height > this.imageEl.OriginHeight ||
28916                     (width < this.minHeight && height < this.minWidth)
28917                 )
28918         ){
28919             return false;
28920         }
28921         
28922         if(
28923                 !this.isDocument &&
28924                 (this.rotate == 0 || this.rotate == 180) && 
28925                 (
28926                     width < this.minWidth || 
28927                     width > this.imageEl.OriginWidth || 
28928                     height < this.minHeight || 
28929                     height > this.imageEl.OriginHeight
28930                 )
28931         ){
28932             return false;
28933         }
28934         
28935         if(
28936                 !this.isDocument &&
28937                 (this.rotate == 90 || this.rotate == 270) && 
28938                 (
28939                     width < this.minHeight || 
28940                     width > this.imageEl.OriginWidth || 
28941                     height < this.minWidth || 
28942                     height > this.imageEl.OriginHeight
28943                 )
28944         ){
28945             return false;
28946         }
28947         
28948         return true;
28949         
28950     },
28951     
28952     onRotateLeft : function(e)
28953     {   
28954         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28955             
28956             var minScale = this.thumbEl.getWidth() / this.minWidth;
28957             
28958             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28959             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28960             
28961             this.startScale = this.scale;
28962             
28963             while (this.getScaleLevel() < minScale){
28964             
28965                 this.scale = this.scale + 1;
28966                 
28967                 if(!this.zoomable()){
28968                     break;
28969                 }
28970                 
28971                 if(
28972                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28973                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28974                 ){
28975                     continue;
28976                 }
28977                 
28978                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28979
28980                 this.draw();
28981                 
28982                 return;
28983             }
28984             
28985             this.scale = this.startScale;
28986             
28987             this.onRotateFail();
28988             
28989             return false;
28990         }
28991         
28992         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28993
28994         if(this.isDocument){
28995             this.setThumbBoxSize();
28996             this.setThumbBoxPosition();
28997             this.setCanvasPosition();
28998         }
28999         
29000         this.draw();
29001         
29002         this.fireEvent('rotate', this, 'left');
29003         
29004     },
29005     
29006     onRotateRight : function(e)
29007     {
29008         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29009             
29010             var minScale = this.thumbEl.getWidth() / this.minWidth;
29011         
29012             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29013             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29014             
29015             this.startScale = this.scale;
29016             
29017             while (this.getScaleLevel() < minScale){
29018             
29019                 this.scale = this.scale + 1;
29020                 
29021                 if(!this.zoomable()){
29022                     break;
29023                 }
29024                 
29025                 if(
29026                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29027                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29028                 ){
29029                     continue;
29030                 }
29031                 
29032                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29033
29034                 this.draw();
29035                 
29036                 return;
29037             }
29038             
29039             this.scale = this.startScale;
29040             
29041             this.onRotateFail();
29042             
29043             return false;
29044         }
29045         
29046         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29047
29048         if(this.isDocument){
29049             this.setThumbBoxSize();
29050             this.setThumbBoxPosition();
29051             this.setCanvasPosition();
29052         }
29053         
29054         this.draw();
29055         
29056         this.fireEvent('rotate', this, 'right');
29057     },
29058     
29059     onRotateFail : function()
29060     {
29061         this.errorEl.show(true);
29062         
29063         var _this = this;
29064         
29065         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29066     },
29067     
29068     draw : function()
29069     {
29070         this.previewEl.dom.innerHTML = '';
29071         
29072         var canvasEl = document.createElement("canvas");
29073         
29074         var contextEl = canvasEl.getContext("2d");
29075         
29076         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29077         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29078         var center = this.imageEl.OriginWidth / 2;
29079         
29080         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29081             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29082             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29083             center = this.imageEl.OriginHeight / 2;
29084         }
29085         
29086         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29087         
29088         contextEl.translate(center, center);
29089         contextEl.rotate(this.rotate * Math.PI / 180);
29090
29091         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29092         
29093         this.canvasEl = document.createElement("canvas");
29094         
29095         this.contextEl = this.canvasEl.getContext("2d");
29096         
29097         switch (this.rotate) {
29098             case 0 :
29099                 
29100                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29101                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29102                 
29103                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29104                 
29105                 break;
29106             case 90 : 
29107                 
29108                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29109                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29110                 
29111                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29112                     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);
29113                     break;
29114                 }
29115                 
29116                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29117                 
29118                 break;
29119             case 180 :
29120                 
29121                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29122                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29123                 
29124                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29125                     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);
29126                     break;
29127                 }
29128                 
29129                 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);
29130                 
29131                 break;
29132             case 270 :
29133                 
29134                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29135                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29136         
29137                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29138                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29139                     break;
29140                 }
29141                 
29142                 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);
29143                 
29144                 break;
29145             default : 
29146                 break;
29147         }
29148         
29149         this.previewEl.appendChild(this.canvasEl);
29150         
29151         this.setCanvasPosition();
29152     },
29153     
29154     crop : function()
29155     {
29156         if(!this.canvasLoaded){
29157             return;
29158         }
29159         
29160         var imageCanvas = document.createElement("canvas");
29161         
29162         var imageContext = imageCanvas.getContext("2d");
29163         
29164         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29165         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29166         
29167         var center = imageCanvas.width / 2;
29168         
29169         imageContext.translate(center, center);
29170         
29171         imageContext.rotate(this.rotate * Math.PI / 180);
29172         
29173         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29174         
29175         var canvas = document.createElement("canvas");
29176         
29177         var context = canvas.getContext("2d");
29178                 
29179         canvas.width = this.minWidth;
29180         canvas.height = this.minHeight;
29181
29182         switch (this.rotate) {
29183             case 0 :
29184                 
29185                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29186                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29187                 
29188                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29189                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29190                 
29191                 var targetWidth = this.minWidth - 2 * x;
29192                 var targetHeight = this.minHeight - 2 * y;
29193                 
29194                 var scale = 1;
29195                 
29196                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29197                     scale = targetWidth / width;
29198                 }
29199                 
29200                 if(x > 0 && y == 0){
29201                     scale = targetHeight / height;
29202                 }
29203                 
29204                 if(x > 0 && y > 0){
29205                     scale = targetWidth / width;
29206                     
29207                     if(width < height){
29208                         scale = targetHeight / height;
29209                     }
29210                 }
29211                 
29212                 context.scale(scale, scale);
29213                 
29214                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29215                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29216
29217                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29218                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29219
29220                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29221                 
29222                 break;
29223             case 90 : 
29224                 
29225                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29226                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29227                 
29228                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29229                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29230                 
29231                 var targetWidth = this.minWidth - 2 * x;
29232                 var targetHeight = this.minHeight - 2 * y;
29233                 
29234                 var scale = 1;
29235                 
29236                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29237                     scale = targetWidth / width;
29238                 }
29239                 
29240                 if(x > 0 && y == 0){
29241                     scale = targetHeight / height;
29242                 }
29243                 
29244                 if(x > 0 && y > 0){
29245                     scale = targetWidth / width;
29246                     
29247                     if(width < height){
29248                         scale = targetHeight / height;
29249                     }
29250                 }
29251                 
29252                 context.scale(scale, scale);
29253                 
29254                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29255                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29256
29257                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29258                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29259                 
29260                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29261                 
29262                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29263                 
29264                 break;
29265             case 180 :
29266                 
29267                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29268                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29269                 
29270                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29271                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29272                 
29273                 var targetWidth = this.minWidth - 2 * x;
29274                 var targetHeight = this.minHeight - 2 * y;
29275                 
29276                 var scale = 1;
29277                 
29278                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29279                     scale = targetWidth / width;
29280                 }
29281                 
29282                 if(x > 0 && y == 0){
29283                     scale = targetHeight / height;
29284                 }
29285                 
29286                 if(x > 0 && y > 0){
29287                     scale = targetWidth / width;
29288                     
29289                     if(width < height){
29290                         scale = targetHeight / height;
29291                     }
29292                 }
29293                 
29294                 context.scale(scale, scale);
29295                 
29296                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29297                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29298
29299                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29300                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29301
29302                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29303                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29304                 
29305                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29306                 
29307                 break;
29308             case 270 :
29309                 
29310                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29311                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29312                 
29313                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29314                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29315                 
29316                 var targetWidth = this.minWidth - 2 * x;
29317                 var targetHeight = this.minHeight - 2 * y;
29318                 
29319                 var scale = 1;
29320                 
29321                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29322                     scale = targetWidth / width;
29323                 }
29324                 
29325                 if(x > 0 && y == 0){
29326                     scale = targetHeight / height;
29327                 }
29328                 
29329                 if(x > 0 && y > 0){
29330                     scale = targetWidth / width;
29331                     
29332                     if(width < height){
29333                         scale = targetHeight / height;
29334                     }
29335                 }
29336                 
29337                 context.scale(scale, scale);
29338                 
29339                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29340                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29341
29342                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29343                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29344                 
29345                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29346                 
29347                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29348                 
29349                 break;
29350             default : 
29351                 break;
29352         }
29353         
29354         this.cropData = canvas.toDataURL(this.cropType);
29355         
29356         if(this.fireEvent('crop', this, this.cropData) !== false){
29357             this.process(this.file, this.cropData);
29358         }
29359         
29360         return;
29361         
29362     },
29363     
29364     setThumbBoxSize : function()
29365     {
29366         var width, height;
29367         
29368         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29369             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29370             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29371             
29372             this.minWidth = width;
29373             this.minHeight = height;
29374             
29375             if(this.rotate == 90 || this.rotate == 270){
29376                 this.minWidth = height;
29377                 this.minHeight = width;
29378             }
29379         }
29380         
29381         height = 300;
29382         width = Math.ceil(this.minWidth * height / this.minHeight);
29383         
29384         if(this.minWidth > this.minHeight){
29385             width = 300;
29386             height = Math.ceil(this.minHeight * width / this.minWidth);
29387         }
29388         
29389         this.thumbEl.setStyle({
29390             width : width + 'px',
29391             height : height + 'px'
29392         });
29393
29394         return;
29395             
29396     },
29397     
29398     setThumbBoxPosition : function()
29399     {
29400         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29401         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29402         
29403         this.thumbEl.setLeft(x);
29404         this.thumbEl.setTop(y);
29405         
29406     },
29407     
29408     baseRotateLevel : function()
29409     {
29410         this.baseRotate = 1;
29411         
29412         if(
29413                 typeof(this.exif) != 'undefined' &&
29414                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29415                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29416         ){
29417             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29418         }
29419         
29420         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29421         
29422     },
29423     
29424     baseScaleLevel : function()
29425     {
29426         var width, height;
29427         
29428         if(this.isDocument){
29429             
29430             if(this.baseRotate == 6 || this.baseRotate == 8){
29431             
29432                 height = this.thumbEl.getHeight();
29433                 this.baseScale = height / this.imageEl.OriginWidth;
29434
29435                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29436                     width = this.thumbEl.getWidth();
29437                     this.baseScale = width / this.imageEl.OriginHeight;
29438                 }
29439
29440                 return;
29441             }
29442
29443             height = this.thumbEl.getHeight();
29444             this.baseScale = height / this.imageEl.OriginHeight;
29445
29446             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29447                 width = this.thumbEl.getWidth();
29448                 this.baseScale = width / this.imageEl.OriginWidth;
29449             }
29450
29451             return;
29452         }
29453         
29454         if(this.baseRotate == 6 || this.baseRotate == 8){
29455             
29456             width = this.thumbEl.getHeight();
29457             this.baseScale = width / this.imageEl.OriginHeight;
29458             
29459             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29460                 height = this.thumbEl.getWidth();
29461                 this.baseScale = height / this.imageEl.OriginHeight;
29462             }
29463             
29464             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29465                 height = this.thumbEl.getWidth();
29466                 this.baseScale = height / this.imageEl.OriginHeight;
29467                 
29468                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29469                     width = this.thumbEl.getHeight();
29470                     this.baseScale = width / this.imageEl.OriginWidth;
29471                 }
29472             }
29473             
29474             return;
29475         }
29476         
29477         width = this.thumbEl.getWidth();
29478         this.baseScale = width / this.imageEl.OriginWidth;
29479         
29480         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29481             height = this.thumbEl.getHeight();
29482             this.baseScale = height / this.imageEl.OriginHeight;
29483         }
29484         
29485         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29486             
29487             height = this.thumbEl.getHeight();
29488             this.baseScale = height / this.imageEl.OriginHeight;
29489             
29490             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29491                 width = this.thumbEl.getWidth();
29492                 this.baseScale = width / this.imageEl.OriginWidth;
29493             }
29494             
29495         }
29496         
29497         return;
29498     },
29499     
29500     getScaleLevel : function()
29501     {
29502         return this.baseScale * Math.pow(1.1, this.scale);
29503     },
29504     
29505     onTouchStart : function(e)
29506     {
29507         if(!this.canvasLoaded){
29508             this.beforeSelectFile(e);
29509             return;
29510         }
29511         
29512         var touches = e.browserEvent.touches;
29513         
29514         if(!touches){
29515             return;
29516         }
29517         
29518         if(touches.length == 1){
29519             this.onMouseDown(e);
29520             return;
29521         }
29522         
29523         if(touches.length != 2){
29524             return;
29525         }
29526         
29527         var coords = [];
29528         
29529         for(var i = 0, finger; finger = touches[i]; i++){
29530             coords.push(finger.pageX, finger.pageY);
29531         }
29532         
29533         var x = Math.pow(coords[0] - coords[2], 2);
29534         var y = Math.pow(coords[1] - coords[3], 2);
29535         
29536         this.startDistance = Math.sqrt(x + y);
29537         
29538         this.startScale = this.scale;
29539         
29540         this.pinching = true;
29541         this.dragable = false;
29542         
29543     },
29544     
29545     onTouchMove : function(e)
29546     {
29547         if(!this.pinching && !this.dragable){
29548             return;
29549         }
29550         
29551         var touches = e.browserEvent.touches;
29552         
29553         if(!touches){
29554             return;
29555         }
29556         
29557         if(this.dragable){
29558             this.onMouseMove(e);
29559             return;
29560         }
29561         
29562         var coords = [];
29563         
29564         for(var i = 0, finger; finger = touches[i]; i++){
29565             coords.push(finger.pageX, finger.pageY);
29566         }
29567         
29568         var x = Math.pow(coords[0] - coords[2], 2);
29569         var y = Math.pow(coords[1] - coords[3], 2);
29570         
29571         this.endDistance = Math.sqrt(x + y);
29572         
29573         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29574         
29575         if(!this.zoomable()){
29576             this.scale = this.startScale;
29577             return;
29578         }
29579         
29580         this.draw();
29581         
29582     },
29583     
29584     onTouchEnd : function(e)
29585     {
29586         this.pinching = false;
29587         this.dragable = false;
29588         
29589     },
29590     
29591     process : function(file, crop)
29592     {
29593         if(this.loadMask){
29594             this.maskEl.mask(this.loadingText);
29595         }
29596         
29597         this.xhr = new XMLHttpRequest();
29598         
29599         file.xhr = this.xhr;
29600
29601         this.xhr.open(this.method, this.url, true);
29602         
29603         var headers = {
29604             "Accept": "application/json",
29605             "Cache-Control": "no-cache",
29606             "X-Requested-With": "XMLHttpRequest"
29607         };
29608         
29609         for (var headerName in headers) {
29610             var headerValue = headers[headerName];
29611             if (headerValue) {
29612                 this.xhr.setRequestHeader(headerName, headerValue);
29613             }
29614         }
29615         
29616         var _this = this;
29617         
29618         this.xhr.onload = function()
29619         {
29620             _this.xhrOnLoad(_this.xhr);
29621         }
29622         
29623         this.xhr.onerror = function()
29624         {
29625             _this.xhrOnError(_this.xhr);
29626         }
29627         
29628         var formData = new FormData();
29629
29630         formData.append('returnHTML', 'NO');
29631         
29632         if(crop){
29633             formData.append('crop', crop);
29634         }
29635         
29636         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29637             formData.append(this.paramName, file, file.name);
29638         }
29639         
29640         if(typeof(file.filename) != 'undefined'){
29641             formData.append('filename', file.filename);
29642         }
29643         
29644         if(typeof(file.mimetype) != 'undefined'){
29645             formData.append('mimetype', file.mimetype);
29646         }
29647         
29648         if(this.fireEvent('arrange', this, formData) != false){
29649             this.xhr.send(formData);
29650         };
29651     },
29652     
29653     xhrOnLoad : function(xhr)
29654     {
29655         if(this.loadMask){
29656             this.maskEl.unmask();
29657         }
29658         
29659         if (xhr.readyState !== 4) {
29660             this.fireEvent('exception', this, xhr);
29661             return;
29662         }
29663
29664         var response = Roo.decode(xhr.responseText);
29665         
29666         if(!response.success){
29667             this.fireEvent('exception', this, xhr);
29668             return;
29669         }
29670         
29671         var response = Roo.decode(xhr.responseText);
29672         
29673         this.fireEvent('upload', this, response);
29674         
29675     },
29676     
29677     xhrOnError : function()
29678     {
29679         if(this.loadMask){
29680             this.maskEl.unmask();
29681         }
29682         
29683         Roo.log('xhr on error');
29684         
29685         var response = Roo.decode(xhr.responseText);
29686           
29687         Roo.log(response);
29688         
29689     },
29690     
29691     prepare : function(file)
29692     {   
29693         if(this.loadMask){
29694             this.maskEl.mask(this.loadingText);
29695         }
29696         
29697         this.file = false;
29698         this.exif = {};
29699         
29700         if(typeof(file) === 'string'){
29701             this.loadCanvas(file);
29702             return;
29703         }
29704         
29705         if(!file || !this.urlAPI){
29706             return;
29707         }
29708         
29709         this.file = file;
29710         this.cropType = file.type;
29711         
29712         var _this = this;
29713         
29714         if(this.fireEvent('prepare', this, this.file) != false){
29715             
29716             var reader = new FileReader();
29717             
29718             reader.onload = function (e) {
29719                 if (e.target.error) {
29720                     Roo.log(e.target.error);
29721                     return;
29722                 }
29723                 
29724                 var buffer = e.target.result,
29725                     dataView = new DataView(buffer),
29726                     offset = 2,
29727                     maxOffset = dataView.byteLength - 4,
29728                     markerBytes,
29729                     markerLength;
29730                 
29731                 if (dataView.getUint16(0) === 0xffd8) {
29732                     while (offset < maxOffset) {
29733                         markerBytes = dataView.getUint16(offset);
29734                         
29735                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29736                             markerLength = dataView.getUint16(offset + 2) + 2;
29737                             if (offset + markerLength > dataView.byteLength) {
29738                                 Roo.log('Invalid meta data: Invalid segment size.');
29739                                 break;
29740                             }
29741                             
29742                             if(markerBytes == 0xffe1){
29743                                 _this.parseExifData(
29744                                     dataView,
29745                                     offset,
29746                                     markerLength
29747                                 );
29748                             }
29749                             
29750                             offset += markerLength;
29751                             
29752                             continue;
29753                         }
29754                         
29755                         break;
29756                     }
29757                     
29758                 }
29759                 
29760                 var url = _this.urlAPI.createObjectURL(_this.file);
29761                 
29762                 _this.loadCanvas(url);
29763                 
29764                 return;
29765             }
29766             
29767             reader.readAsArrayBuffer(this.file);
29768             
29769         }
29770         
29771     },
29772     
29773     parseExifData : function(dataView, offset, length)
29774     {
29775         var tiffOffset = offset + 10,
29776             littleEndian,
29777             dirOffset;
29778     
29779         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29780             // No Exif data, might be XMP data instead
29781             return;
29782         }
29783         
29784         // Check for the ASCII code for "Exif" (0x45786966):
29785         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29786             // No Exif data, might be XMP data instead
29787             return;
29788         }
29789         if (tiffOffset + 8 > dataView.byteLength) {
29790             Roo.log('Invalid Exif data: Invalid segment size.');
29791             return;
29792         }
29793         // Check for the two null bytes:
29794         if (dataView.getUint16(offset + 8) !== 0x0000) {
29795             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29796             return;
29797         }
29798         // Check the byte alignment:
29799         switch (dataView.getUint16(tiffOffset)) {
29800         case 0x4949:
29801             littleEndian = true;
29802             break;
29803         case 0x4D4D:
29804             littleEndian = false;
29805             break;
29806         default:
29807             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29808             return;
29809         }
29810         // Check for the TIFF tag marker (0x002A):
29811         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29812             Roo.log('Invalid Exif data: Missing TIFF marker.');
29813             return;
29814         }
29815         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29816         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29817         
29818         this.parseExifTags(
29819             dataView,
29820             tiffOffset,
29821             tiffOffset + dirOffset,
29822             littleEndian
29823         );
29824     },
29825     
29826     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29827     {
29828         var tagsNumber,
29829             dirEndOffset,
29830             i;
29831         if (dirOffset + 6 > dataView.byteLength) {
29832             Roo.log('Invalid Exif data: Invalid directory offset.');
29833             return;
29834         }
29835         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29836         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29837         if (dirEndOffset + 4 > dataView.byteLength) {
29838             Roo.log('Invalid Exif data: Invalid directory size.');
29839             return;
29840         }
29841         for (i = 0; i < tagsNumber; i += 1) {
29842             this.parseExifTag(
29843                 dataView,
29844                 tiffOffset,
29845                 dirOffset + 2 + 12 * i, // tag offset
29846                 littleEndian
29847             );
29848         }
29849         // Return the offset to the next directory:
29850         return dataView.getUint32(dirEndOffset, littleEndian);
29851     },
29852     
29853     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29854     {
29855         var tag = dataView.getUint16(offset, littleEndian);
29856         
29857         this.exif[tag] = this.getExifValue(
29858             dataView,
29859             tiffOffset,
29860             offset,
29861             dataView.getUint16(offset + 2, littleEndian), // tag type
29862             dataView.getUint32(offset + 4, littleEndian), // tag length
29863             littleEndian
29864         );
29865     },
29866     
29867     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29868     {
29869         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29870             tagSize,
29871             dataOffset,
29872             values,
29873             i,
29874             str,
29875             c;
29876     
29877         if (!tagType) {
29878             Roo.log('Invalid Exif data: Invalid tag type.');
29879             return;
29880         }
29881         
29882         tagSize = tagType.size * length;
29883         // Determine if the value is contained in the dataOffset bytes,
29884         // or if the value at the dataOffset is a pointer to the actual data:
29885         dataOffset = tagSize > 4 ?
29886                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29887         if (dataOffset + tagSize > dataView.byteLength) {
29888             Roo.log('Invalid Exif data: Invalid data offset.');
29889             return;
29890         }
29891         if (length === 1) {
29892             return tagType.getValue(dataView, dataOffset, littleEndian);
29893         }
29894         values = [];
29895         for (i = 0; i < length; i += 1) {
29896             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29897         }
29898         
29899         if (tagType.ascii) {
29900             str = '';
29901             // Concatenate the chars:
29902             for (i = 0; i < values.length; i += 1) {
29903                 c = values[i];
29904                 // Ignore the terminating NULL byte(s):
29905                 if (c === '\u0000') {
29906                     break;
29907                 }
29908                 str += c;
29909             }
29910             return str;
29911         }
29912         return values;
29913     }
29914     
29915 });
29916
29917 Roo.apply(Roo.bootstrap.UploadCropbox, {
29918     tags : {
29919         'Orientation': 0x0112
29920     },
29921     
29922     Orientation: {
29923             1: 0, //'top-left',
29924 //            2: 'top-right',
29925             3: 180, //'bottom-right',
29926 //            4: 'bottom-left',
29927 //            5: 'left-top',
29928             6: 90, //'right-top',
29929 //            7: 'right-bottom',
29930             8: 270 //'left-bottom'
29931     },
29932     
29933     exifTagTypes : {
29934         // byte, 8-bit unsigned int:
29935         1: {
29936             getValue: function (dataView, dataOffset) {
29937                 return dataView.getUint8(dataOffset);
29938             },
29939             size: 1
29940         },
29941         // ascii, 8-bit byte:
29942         2: {
29943             getValue: function (dataView, dataOffset) {
29944                 return String.fromCharCode(dataView.getUint8(dataOffset));
29945             },
29946             size: 1,
29947             ascii: true
29948         },
29949         // short, 16 bit int:
29950         3: {
29951             getValue: function (dataView, dataOffset, littleEndian) {
29952                 return dataView.getUint16(dataOffset, littleEndian);
29953             },
29954             size: 2
29955         },
29956         // long, 32 bit int:
29957         4: {
29958             getValue: function (dataView, dataOffset, littleEndian) {
29959                 return dataView.getUint32(dataOffset, littleEndian);
29960             },
29961             size: 4
29962         },
29963         // rational = two long values, first is numerator, second is denominator:
29964         5: {
29965             getValue: function (dataView, dataOffset, littleEndian) {
29966                 return dataView.getUint32(dataOffset, littleEndian) /
29967                     dataView.getUint32(dataOffset + 4, littleEndian);
29968             },
29969             size: 8
29970         },
29971         // slong, 32 bit signed int:
29972         9: {
29973             getValue: function (dataView, dataOffset, littleEndian) {
29974                 return dataView.getInt32(dataOffset, littleEndian);
29975             },
29976             size: 4
29977         },
29978         // srational, two slongs, first is numerator, second is denominator:
29979         10: {
29980             getValue: function (dataView, dataOffset, littleEndian) {
29981                 return dataView.getInt32(dataOffset, littleEndian) /
29982                     dataView.getInt32(dataOffset + 4, littleEndian);
29983             },
29984             size: 8
29985         }
29986     },
29987     
29988     footer : {
29989         STANDARD : [
29990             {
29991                 tag : 'div',
29992                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29993                 action : 'rotate-left',
29994                 cn : [
29995                     {
29996                         tag : 'button',
29997                         cls : 'btn btn-default',
29998                         html : '<i class="fa fa-undo"></i>'
29999                     }
30000                 ]
30001             },
30002             {
30003                 tag : 'div',
30004                 cls : 'btn-group roo-upload-cropbox-picture',
30005                 action : 'picture',
30006                 cn : [
30007                     {
30008                         tag : 'button',
30009                         cls : 'btn btn-default',
30010                         html : '<i class="fa fa-picture-o"></i>'
30011                     }
30012                 ]
30013             },
30014             {
30015                 tag : 'div',
30016                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30017                 action : 'rotate-right',
30018                 cn : [
30019                     {
30020                         tag : 'button',
30021                         cls : 'btn btn-default',
30022                         html : '<i class="fa fa-repeat"></i>'
30023                     }
30024                 ]
30025             }
30026         ],
30027         DOCUMENT : [
30028             {
30029                 tag : 'div',
30030                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30031                 action : 'rotate-left',
30032                 cn : [
30033                     {
30034                         tag : 'button',
30035                         cls : 'btn btn-default',
30036                         html : '<i class="fa fa-undo"></i>'
30037                     }
30038                 ]
30039             },
30040             {
30041                 tag : 'div',
30042                 cls : 'btn-group roo-upload-cropbox-download',
30043                 action : 'download',
30044                 cn : [
30045                     {
30046                         tag : 'button',
30047                         cls : 'btn btn-default',
30048                         html : '<i class="fa fa-download"></i>'
30049                     }
30050                 ]
30051             },
30052             {
30053                 tag : 'div',
30054                 cls : 'btn-group roo-upload-cropbox-crop',
30055                 action : 'crop',
30056                 cn : [
30057                     {
30058                         tag : 'button',
30059                         cls : 'btn btn-default',
30060                         html : '<i class="fa fa-crop"></i>'
30061                     }
30062                 ]
30063             },
30064             {
30065                 tag : 'div',
30066                 cls : 'btn-group roo-upload-cropbox-trash',
30067                 action : 'trash',
30068                 cn : [
30069                     {
30070                         tag : 'button',
30071                         cls : 'btn btn-default',
30072                         html : '<i class="fa fa-trash"></i>'
30073                     }
30074                 ]
30075             },
30076             {
30077                 tag : 'div',
30078                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30079                 action : 'rotate-right',
30080                 cn : [
30081                     {
30082                         tag : 'button',
30083                         cls : 'btn btn-default',
30084                         html : '<i class="fa fa-repeat"></i>'
30085                     }
30086                 ]
30087             }
30088         ],
30089         ROTATOR : [
30090             {
30091                 tag : 'div',
30092                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30093                 action : 'rotate-left',
30094                 cn : [
30095                     {
30096                         tag : 'button',
30097                         cls : 'btn btn-default',
30098                         html : '<i class="fa fa-undo"></i>'
30099                     }
30100                 ]
30101             },
30102             {
30103                 tag : 'div',
30104                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30105                 action : 'rotate-right',
30106                 cn : [
30107                     {
30108                         tag : 'button',
30109                         cls : 'btn btn-default',
30110                         html : '<i class="fa fa-repeat"></i>'
30111                     }
30112                 ]
30113             }
30114         ]
30115     }
30116 });
30117
30118 /*
30119 * Licence: LGPL
30120 */
30121
30122 /**
30123  * @class Roo.bootstrap.DocumentManager
30124  * @extends Roo.bootstrap.Component
30125  * Bootstrap DocumentManager class
30126  * @cfg {String} paramName default 'imageUpload'
30127  * @cfg {String} toolTipName default 'filename'
30128  * @cfg {String} method default POST
30129  * @cfg {String} url action url
30130  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30131  * @cfg {Boolean} multiple multiple upload default true
30132  * @cfg {Number} thumbSize default 300
30133  * @cfg {String} fieldLabel
30134  * @cfg {Number} labelWidth default 4
30135  * @cfg {String} labelAlign (left|top) default left
30136  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30137 * @cfg {Number} labellg set the width of label (1-12)
30138  * @cfg {Number} labelmd set the width of label (1-12)
30139  * @cfg {Number} labelsm set the width of label (1-12)
30140  * @cfg {Number} labelxs set the width of label (1-12)
30141  * 
30142  * @constructor
30143  * Create a new DocumentManager
30144  * @param {Object} config The config object
30145  */
30146
30147 Roo.bootstrap.DocumentManager = function(config){
30148     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30149     
30150     this.files = [];
30151     this.delegates = [];
30152     
30153     this.addEvents({
30154         /**
30155          * @event initial
30156          * Fire when initial the DocumentManager
30157          * @param {Roo.bootstrap.DocumentManager} this
30158          */
30159         "initial" : true,
30160         /**
30161          * @event inspect
30162          * inspect selected file
30163          * @param {Roo.bootstrap.DocumentManager} this
30164          * @param {File} file
30165          */
30166         "inspect" : true,
30167         /**
30168          * @event exception
30169          * Fire when xhr load exception
30170          * @param {Roo.bootstrap.DocumentManager} this
30171          * @param {XMLHttpRequest} xhr
30172          */
30173         "exception" : true,
30174         /**
30175          * @event afterupload
30176          * Fire when xhr load exception
30177          * @param {Roo.bootstrap.DocumentManager} this
30178          * @param {XMLHttpRequest} xhr
30179          */
30180         "afterupload" : true,
30181         /**
30182          * @event prepare
30183          * prepare the form data
30184          * @param {Roo.bootstrap.DocumentManager} this
30185          * @param {Object} formData
30186          */
30187         "prepare" : true,
30188         /**
30189          * @event remove
30190          * Fire when remove the file
30191          * @param {Roo.bootstrap.DocumentManager} this
30192          * @param {Object} file
30193          */
30194         "remove" : true,
30195         /**
30196          * @event refresh
30197          * Fire after refresh the file
30198          * @param {Roo.bootstrap.DocumentManager} this
30199          */
30200         "refresh" : true,
30201         /**
30202          * @event click
30203          * Fire after click the image
30204          * @param {Roo.bootstrap.DocumentManager} this
30205          * @param {Object} file
30206          */
30207         "click" : true,
30208         /**
30209          * @event edit
30210          * Fire when upload a image and editable set to true
30211          * @param {Roo.bootstrap.DocumentManager} this
30212          * @param {Object} file
30213          */
30214         "edit" : true,
30215         /**
30216          * @event beforeselectfile
30217          * Fire before select file
30218          * @param {Roo.bootstrap.DocumentManager} this
30219          */
30220         "beforeselectfile" : true,
30221         /**
30222          * @event process
30223          * Fire before process file
30224          * @param {Roo.bootstrap.DocumentManager} this
30225          * @param {Object} file
30226          */
30227         "process" : true,
30228         /**
30229          * @event previewrendered
30230          * Fire when preview rendered
30231          * @param {Roo.bootstrap.DocumentManager} this
30232          * @param {Object} file
30233          */
30234         "previewrendered" : true,
30235         /**
30236          */
30237         "previewResize" : true
30238         
30239     });
30240 };
30241
30242 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30243     
30244     boxes : 0,
30245     inputName : '',
30246     thumbSize : 300,
30247     multiple : true,
30248     files : false,
30249     method : 'POST',
30250     url : '',
30251     paramName : 'imageUpload',
30252     toolTipName : 'filename',
30253     fieldLabel : '',
30254     labelWidth : 4,
30255     labelAlign : 'left',
30256     editable : true,
30257     delegates : false,
30258     xhr : false, 
30259     
30260     labellg : 0,
30261     labelmd : 0,
30262     labelsm : 0,
30263     labelxs : 0,
30264     
30265     getAutoCreate : function()
30266     {   
30267         var managerWidget = {
30268             tag : 'div',
30269             cls : 'roo-document-manager',
30270             cn : [
30271                 {
30272                     tag : 'input',
30273                     cls : 'roo-document-manager-selector',
30274                     type : 'file'
30275                 },
30276                 {
30277                     tag : 'div',
30278                     cls : 'roo-document-manager-uploader',
30279                     cn : [
30280                         {
30281                             tag : 'div',
30282                             cls : 'roo-document-manager-upload-btn',
30283                             html : '<i class="fa fa-plus"></i>'
30284                         }
30285                     ]
30286                     
30287                 }
30288             ]
30289         };
30290         
30291         var content = [
30292             {
30293                 tag : 'div',
30294                 cls : 'column col-md-12',
30295                 cn : managerWidget
30296             }
30297         ];
30298         
30299         if(this.fieldLabel.length){
30300             
30301             content = [
30302                 {
30303                     tag : 'div',
30304                     cls : 'column col-md-12',
30305                     html : this.fieldLabel
30306                 },
30307                 {
30308                     tag : 'div',
30309                     cls : 'column col-md-12',
30310                     cn : managerWidget
30311                 }
30312             ];
30313
30314             if(this.labelAlign == 'left'){
30315                 content = [
30316                     {
30317                         tag : 'div',
30318                         cls : 'column',
30319                         html : this.fieldLabel
30320                     },
30321                     {
30322                         tag : 'div',
30323                         cls : 'column',
30324                         cn : managerWidget
30325                     }
30326                 ];
30327                 
30328                 if(this.labelWidth > 12){
30329                     content[0].style = "width: " + this.labelWidth + 'px';
30330                 }
30331
30332                 if(this.labelWidth < 13 && this.labelmd == 0){
30333                     this.labelmd = this.labelWidth;
30334                 }
30335
30336                 if(this.labellg > 0){
30337                     content[0].cls += ' col-lg-' + this.labellg;
30338                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30339                 }
30340
30341                 if(this.labelmd > 0){
30342                     content[0].cls += ' col-md-' + this.labelmd;
30343                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30344                 }
30345
30346                 if(this.labelsm > 0){
30347                     content[0].cls += ' col-sm-' + this.labelsm;
30348                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30349                 }
30350
30351                 if(this.labelxs > 0){
30352                     content[0].cls += ' col-xs-' + this.labelxs;
30353                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30354                 }
30355                 
30356             }
30357         }
30358         
30359         var cfg = {
30360             tag : 'div',
30361             cls : 'row clearfix',
30362             cn : content
30363         };
30364         
30365         return cfg;
30366         
30367     },
30368     
30369     initEvents : function()
30370     {
30371         this.managerEl = this.el.select('.roo-document-manager', true).first();
30372         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30373         
30374         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30375         this.selectorEl.hide();
30376         
30377         if(this.multiple){
30378             this.selectorEl.attr('multiple', 'multiple');
30379         }
30380         
30381         this.selectorEl.on('change', this.onFileSelected, this);
30382         
30383         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30384         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30385         
30386         this.uploader.on('click', this.onUploaderClick, this);
30387         
30388         this.renderProgressDialog();
30389         
30390         var _this = this;
30391         
30392         window.addEventListener("resize", function() { _this.refresh(); } );
30393         
30394         this.fireEvent('initial', this);
30395     },
30396     
30397     renderProgressDialog : function()
30398     {
30399         var _this = this;
30400         
30401         this.progressDialog = new Roo.bootstrap.Modal({
30402             cls : 'roo-document-manager-progress-dialog',
30403             allow_close : false,
30404             animate : false,
30405             title : '',
30406             buttons : [
30407                 {
30408                     name  :'cancel',
30409                     weight : 'danger',
30410                     html : 'Cancel'
30411                 }
30412             ], 
30413             listeners : { 
30414                 btnclick : function() {
30415                     _this.uploadCancel();
30416                     this.hide();
30417                 }
30418             }
30419         });
30420          
30421         this.progressDialog.render(Roo.get(document.body));
30422          
30423         this.progress = new Roo.bootstrap.Progress({
30424             cls : 'roo-document-manager-progress',
30425             active : true,
30426             striped : true
30427         });
30428         
30429         this.progress.render(this.progressDialog.getChildContainer());
30430         
30431         this.progressBar = new Roo.bootstrap.ProgressBar({
30432             cls : 'roo-document-manager-progress-bar',
30433             aria_valuenow : 0,
30434             aria_valuemin : 0,
30435             aria_valuemax : 12,
30436             panel : 'success'
30437         });
30438         
30439         this.progressBar.render(this.progress.getChildContainer());
30440     },
30441     
30442     onUploaderClick : function(e)
30443     {
30444         e.preventDefault();
30445      
30446         if(this.fireEvent('beforeselectfile', this) != false){
30447             this.selectorEl.dom.click();
30448         }
30449         
30450     },
30451     
30452     onFileSelected : function(e)
30453     {
30454         e.preventDefault();
30455         
30456         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30457             return;
30458         }
30459         
30460         Roo.each(this.selectorEl.dom.files, function(file){
30461             if(this.fireEvent('inspect', this, file) != false){
30462                 this.files.push(file);
30463             }
30464         }, this);
30465         
30466         this.queue();
30467         
30468     },
30469     
30470     queue : function()
30471     {
30472         this.selectorEl.dom.value = '';
30473         
30474         if(!this.files || !this.files.length){
30475             return;
30476         }
30477         
30478         if(this.boxes > 0 && this.files.length > this.boxes){
30479             this.files = this.files.slice(0, this.boxes);
30480         }
30481         
30482         this.uploader.show();
30483         
30484         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30485             this.uploader.hide();
30486         }
30487         
30488         var _this = this;
30489         
30490         var files = [];
30491         
30492         var docs = [];
30493         
30494         Roo.each(this.files, function(file){
30495             
30496             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30497                 var f = this.renderPreview(file);
30498                 files.push(f);
30499                 return;
30500             }
30501             
30502             if(file.type.indexOf('image') != -1){
30503                 this.delegates.push(
30504                     (function(){
30505                         _this.process(file);
30506                     }).createDelegate(this)
30507                 );
30508         
30509                 return;
30510             }
30511             
30512             docs.push(
30513                 (function(){
30514                     _this.process(file);
30515                 }).createDelegate(this)
30516             );
30517             
30518         }, this);
30519         
30520         this.files = files;
30521         
30522         this.delegates = this.delegates.concat(docs);
30523         
30524         if(!this.delegates.length){
30525             this.refresh();
30526             return;
30527         }
30528         
30529         this.progressBar.aria_valuemax = this.delegates.length;
30530         
30531         this.arrange();
30532         
30533         return;
30534     },
30535     
30536     arrange : function()
30537     {
30538         if(!this.delegates.length){
30539             this.progressDialog.hide();
30540             this.refresh();
30541             return;
30542         }
30543         
30544         var delegate = this.delegates.shift();
30545         
30546         this.progressDialog.show();
30547         
30548         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30549         
30550         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30551         
30552         delegate();
30553     },
30554     
30555     refresh : function()
30556     {
30557         this.uploader.show();
30558         
30559         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30560             this.uploader.hide();
30561         }
30562         
30563         Roo.isTouch ? this.closable(false) : this.closable(true);
30564         
30565         this.fireEvent('refresh', this);
30566     },
30567     
30568     onRemove : function(e, el, o)
30569     {
30570         e.preventDefault();
30571         
30572         this.fireEvent('remove', this, o);
30573         
30574     },
30575     
30576     remove : function(o)
30577     {
30578         var files = [];
30579         
30580         Roo.each(this.files, function(file){
30581             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30582                 files.push(file);
30583                 return;
30584             }
30585
30586             o.target.remove();
30587
30588         }, this);
30589         
30590         this.files = files;
30591         
30592         this.refresh();
30593     },
30594     
30595     clear : function()
30596     {
30597         Roo.each(this.files, function(file){
30598             if(!file.target){
30599                 return;
30600             }
30601             
30602             file.target.remove();
30603
30604         }, this);
30605         
30606         this.files = [];
30607         
30608         this.refresh();
30609     },
30610     
30611     onClick : function(e, el, o)
30612     {
30613         e.preventDefault();
30614         
30615         this.fireEvent('click', this, o);
30616         
30617     },
30618     
30619     closable : function(closable)
30620     {
30621         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30622             
30623             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30624             
30625             if(closable){
30626                 el.show();
30627                 return;
30628             }
30629             
30630             el.hide();
30631             
30632         }, this);
30633     },
30634     
30635     xhrOnLoad : function(xhr)
30636     {
30637         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30638             el.remove();
30639         }, this);
30640         
30641         if (xhr.readyState !== 4) {
30642             this.arrange();
30643             this.fireEvent('exception', this, xhr);
30644             return;
30645         }
30646
30647         var response = Roo.decode(xhr.responseText);
30648         
30649         if(!response.success){
30650             this.arrange();
30651             this.fireEvent('exception', this, xhr);
30652             return;
30653         }
30654         
30655         var file = this.renderPreview(response.data);
30656         
30657         this.files.push(file);
30658         
30659         this.arrange();
30660         
30661         this.fireEvent('afterupload', this, xhr);
30662         
30663     },
30664     
30665     xhrOnError : function(xhr)
30666     {
30667         Roo.log('xhr on error');
30668         
30669         var response = Roo.decode(xhr.responseText);
30670           
30671         Roo.log(response);
30672         
30673         this.arrange();
30674     },
30675     
30676     process : function(file)
30677     {
30678         if(this.fireEvent('process', this, file) !== false){
30679             if(this.editable && file.type.indexOf('image') != -1){
30680                 this.fireEvent('edit', this, file);
30681                 return;
30682             }
30683
30684             this.uploadStart(file, false);
30685
30686             return;
30687         }
30688         
30689     },
30690     
30691     uploadStart : function(file, crop)
30692     {
30693         this.xhr = new XMLHttpRequest();
30694         
30695         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30696             this.arrange();
30697             return;
30698         }
30699         
30700         file.xhr = this.xhr;
30701             
30702         this.managerEl.createChild({
30703             tag : 'div',
30704             cls : 'roo-document-manager-loading',
30705             cn : [
30706                 {
30707                     tag : 'div',
30708                     tooltip : file.name,
30709                     cls : 'roo-document-manager-thumb',
30710                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30711                 }
30712             ]
30713
30714         });
30715
30716         this.xhr.open(this.method, this.url, true);
30717         
30718         var headers = {
30719             "Accept": "application/json",
30720             "Cache-Control": "no-cache",
30721             "X-Requested-With": "XMLHttpRequest"
30722         };
30723         
30724         for (var headerName in headers) {
30725             var headerValue = headers[headerName];
30726             if (headerValue) {
30727                 this.xhr.setRequestHeader(headerName, headerValue);
30728             }
30729         }
30730         
30731         var _this = this;
30732         
30733         this.xhr.onload = function()
30734         {
30735             _this.xhrOnLoad(_this.xhr);
30736         }
30737         
30738         this.xhr.onerror = function()
30739         {
30740             _this.xhrOnError(_this.xhr);
30741         }
30742         
30743         var formData = new FormData();
30744
30745         formData.append('returnHTML', 'NO');
30746         
30747         if(crop){
30748             formData.append('crop', crop);
30749         }
30750         
30751         formData.append(this.paramName, file, file.name);
30752         
30753         var options = {
30754             file : file, 
30755             manually : false
30756         };
30757         
30758         if(this.fireEvent('prepare', this, formData, options) != false){
30759             
30760             if(options.manually){
30761                 return;
30762             }
30763             
30764             this.xhr.send(formData);
30765             return;
30766         };
30767         
30768         this.uploadCancel();
30769     },
30770     
30771     uploadCancel : function()
30772     {
30773         if (this.xhr) {
30774             this.xhr.abort();
30775         }
30776         
30777         this.delegates = [];
30778         
30779         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30780             el.remove();
30781         }, this);
30782         
30783         this.arrange();
30784     },
30785     
30786     renderPreview : function(file)
30787     {
30788         if(typeof(file.target) != 'undefined' && file.target){
30789             return file;
30790         }
30791         
30792         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30793         
30794         var previewEl = this.managerEl.createChild({
30795             tag : 'div',
30796             cls : 'roo-document-manager-preview',
30797             cn : [
30798                 {
30799                     tag : 'div',
30800                     tooltip : file[this.toolTipName],
30801                     cls : 'roo-document-manager-thumb',
30802                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30803                 },
30804                 {
30805                     tag : 'button',
30806                     cls : 'close',
30807                     html : '<i class="fa fa-times-circle"></i>'
30808                 }
30809             ]
30810         });
30811
30812         var close = previewEl.select('button.close', true).first();
30813
30814         close.on('click', this.onRemove, this, file);
30815
30816         file.target = previewEl;
30817
30818         var image = previewEl.select('img', true).first();
30819         
30820         var _this = this;
30821         
30822         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30823         
30824         image.on('click', this.onClick, this, file);
30825         
30826         this.fireEvent('previewrendered', this, file);
30827         
30828         return file;
30829         
30830     },
30831     
30832     onPreviewLoad : function(file, image)
30833     {
30834         if(typeof(file.target) == 'undefined' || !file.target){
30835             return;
30836         }
30837         
30838         var width = image.dom.naturalWidth || image.dom.width;
30839         var height = image.dom.naturalHeight || image.dom.height;
30840         
30841         if(!this.previewResize) {
30842             return;
30843         }
30844         
30845         if(width > height){
30846             file.target.addClass('wide');
30847             return;
30848         }
30849         
30850         file.target.addClass('tall');
30851         return;
30852         
30853     },
30854     
30855     uploadFromSource : function(file, crop)
30856     {
30857         this.xhr = new XMLHttpRequest();
30858         
30859         this.managerEl.createChild({
30860             tag : 'div',
30861             cls : 'roo-document-manager-loading',
30862             cn : [
30863                 {
30864                     tag : 'div',
30865                     tooltip : file.name,
30866                     cls : 'roo-document-manager-thumb',
30867                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30868                 }
30869             ]
30870
30871         });
30872
30873         this.xhr.open(this.method, this.url, true);
30874         
30875         var headers = {
30876             "Accept": "application/json",
30877             "Cache-Control": "no-cache",
30878             "X-Requested-With": "XMLHttpRequest"
30879         };
30880         
30881         for (var headerName in headers) {
30882             var headerValue = headers[headerName];
30883             if (headerValue) {
30884                 this.xhr.setRequestHeader(headerName, headerValue);
30885             }
30886         }
30887         
30888         var _this = this;
30889         
30890         this.xhr.onload = function()
30891         {
30892             _this.xhrOnLoad(_this.xhr);
30893         }
30894         
30895         this.xhr.onerror = function()
30896         {
30897             _this.xhrOnError(_this.xhr);
30898         }
30899         
30900         var formData = new FormData();
30901
30902         formData.append('returnHTML', 'NO');
30903         
30904         formData.append('crop', crop);
30905         
30906         if(typeof(file.filename) != 'undefined'){
30907             formData.append('filename', file.filename);
30908         }
30909         
30910         if(typeof(file.mimetype) != 'undefined'){
30911             formData.append('mimetype', file.mimetype);
30912         }
30913         
30914         Roo.log(formData);
30915         
30916         if(this.fireEvent('prepare', this, formData) != false){
30917             this.xhr.send(formData);
30918         };
30919     }
30920 });
30921
30922 /*
30923 * Licence: LGPL
30924 */
30925
30926 /**
30927  * @class Roo.bootstrap.DocumentViewer
30928  * @extends Roo.bootstrap.Component
30929  * Bootstrap DocumentViewer class
30930  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30931  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30932  * 
30933  * @constructor
30934  * Create a new DocumentViewer
30935  * @param {Object} config The config object
30936  */
30937
30938 Roo.bootstrap.DocumentViewer = function(config){
30939     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30940     
30941     this.addEvents({
30942         /**
30943          * @event initial
30944          * Fire after initEvent
30945          * @param {Roo.bootstrap.DocumentViewer} this
30946          */
30947         "initial" : true,
30948         /**
30949          * @event click
30950          * Fire after click
30951          * @param {Roo.bootstrap.DocumentViewer} this
30952          */
30953         "click" : true,
30954         /**
30955          * @event download
30956          * Fire after download button
30957          * @param {Roo.bootstrap.DocumentViewer} this
30958          */
30959         "download" : true,
30960         /**
30961          * @event trash
30962          * Fire after trash button
30963          * @param {Roo.bootstrap.DocumentViewer} this
30964          */
30965         "trash" : true
30966         
30967     });
30968 };
30969
30970 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30971     
30972     showDownload : true,
30973     
30974     showTrash : true,
30975     
30976     getAutoCreate : function()
30977     {
30978         var cfg = {
30979             tag : 'div',
30980             cls : 'roo-document-viewer',
30981             cn : [
30982                 {
30983                     tag : 'div',
30984                     cls : 'roo-document-viewer-body',
30985                     cn : [
30986                         {
30987                             tag : 'div',
30988                             cls : 'roo-document-viewer-thumb',
30989                             cn : [
30990                                 {
30991                                     tag : 'img',
30992                                     cls : 'roo-document-viewer-image'
30993                                 }
30994                             ]
30995                         }
30996                     ]
30997                 },
30998                 {
30999                     tag : 'div',
31000                     cls : 'roo-document-viewer-footer',
31001                     cn : {
31002                         tag : 'div',
31003                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31004                         cn : [
31005                             {
31006                                 tag : 'div',
31007                                 cls : 'btn-group roo-document-viewer-download',
31008                                 cn : [
31009                                     {
31010                                         tag : 'button',
31011                                         cls : 'btn btn-default',
31012                                         html : '<i class="fa fa-download"></i>'
31013                                     }
31014                                 ]
31015                             },
31016                             {
31017                                 tag : 'div',
31018                                 cls : 'btn-group roo-document-viewer-trash',
31019                                 cn : [
31020                                     {
31021                                         tag : 'button',
31022                                         cls : 'btn btn-default',
31023                                         html : '<i class="fa fa-trash"></i>'
31024                                     }
31025                                 ]
31026                             }
31027                         ]
31028                     }
31029                 }
31030             ]
31031         };
31032         
31033         return cfg;
31034     },
31035     
31036     initEvents : function()
31037     {
31038         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31039         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31040         
31041         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31042         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31043         
31044         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31045         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31046         
31047         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31048         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31049         
31050         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31051         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31052         
31053         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31054         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31055         
31056         this.bodyEl.on('click', this.onClick, this);
31057         this.downloadBtn.on('click', this.onDownload, this);
31058         this.trashBtn.on('click', this.onTrash, this);
31059         
31060         this.downloadBtn.hide();
31061         this.trashBtn.hide();
31062         
31063         if(this.showDownload){
31064             this.downloadBtn.show();
31065         }
31066         
31067         if(this.showTrash){
31068             this.trashBtn.show();
31069         }
31070         
31071         if(!this.showDownload && !this.showTrash) {
31072             this.footerEl.hide();
31073         }
31074         
31075     },
31076     
31077     initial : function()
31078     {
31079         this.fireEvent('initial', this);
31080         
31081     },
31082     
31083     onClick : function(e)
31084     {
31085         e.preventDefault();
31086         
31087         this.fireEvent('click', this);
31088     },
31089     
31090     onDownload : function(e)
31091     {
31092         e.preventDefault();
31093         
31094         this.fireEvent('download', this);
31095     },
31096     
31097     onTrash : function(e)
31098     {
31099         e.preventDefault();
31100         
31101         this.fireEvent('trash', this);
31102     }
31103     
31104 });
31105 /*
31106  * - LGPL
31107  *
31108  * nav progress bar
31109  * 
31110  */
31111
31112 /**
31113  * @class Roo.bootstrap.NavProgressBar
31114  * @extends Roo.bootstrap.Component
31115  * Bootstrap NavProgressBar class
31116  * 
31117  * @constructor
31118  * Create a new nav progress bar
31119  * @param {Object} config The config object
31120  */
31121
31122 Roo.bootstrap.NavProgressBar = function(config){
31123     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31124
31125     this.bullets = this.bullets || [];
31126    
31127 //    Roo.bootstrap.NavProgressBar.register(this);
31128      this.addEvents({
31129         /**
31130              * @event changed
31131              * Fires when the active item changes
31132              * @param {Roo.bootstrap.NavProgressBar} this
31133              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31134              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31135          */
31136         'changed': true
31137      });
31138     
31139 };
31140
31141 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31142     
31143     bullets : [],
31144     barItems : [],
31145     
31146     getAutoCreate : function()
31147     {
31148         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31149         
31150         cfg = {
31151             tag : 'div',
31152             cls : 'roo-navigation-bar-group',
31153             cn : [
31154                 {
31155                     tag : 'div',
31156                     cls : 'roo-navigation-top-bar'
31157                 },
31158                 {
31159                     tag : 'div',
31160                     cls : 'roo-navigation-bullets-bar',
31161                     cn : [
31162                         {
31163                             tag : 'ul',
31164                             cls : 'roo-navigation-bar'
31165                         }
31166                     ]
31167                 },
31168                 
31169                 {
31170                     tag : 'div',
31171                     cls : 'roo-navigation-bottom-bar'
31172                 }
31173             ]
31174             
31175         };
31176         
31177         return cfg;
31178         
31179     },
31180     
31181     initEvents: function() 
31182     {
31183         
31184     },
31185     
31186     onRender : function(ct, position) 
31187     {
31188         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31189         
31190         if(this.bullets.length){
31191             Roo.each(this.bullets, function(b){
31192                this.addItem(b);
31193             }, this);
31194         }
31195         
31196         this.format();
31197         
31198     },
31199     
31200     addItem : function(cfg)
31201     {
31202         var item = new Roo.bootstrap.NavProgressItem(cfg);
31203         
31204         item.parentId = this.id;
31205         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31206         
31207         if(cfg.html){
31208             var top = new Roo.bootstrap.Element({
31209                 tag : 'div',
31210                 cls : 'roo-navigation-bar-text'
31211             });
31212             
31213             var bottom = new Roo.bootstrap.Element({
31214                 tag : 'div',
31215                 cls : 'roo-navigation-bar-text'
31216             });
31217             
31218             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31219             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31220             
31221             var topText = new Roo.bootstrap.Element({
31222                 tag : 'span',
31223                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31224             });
31225             
31226             var bottomText = new Roo.bootstrap.Element({
31227                 tag : 'span',
31228                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31229             });
31230             
31231             topText.onRender(top.el, null);
31232             bottomText.onRender(bottom.el, null);
31233             
31234             item.topEl = top;
31235             item.bottomEl = bottom;
31236         }
31237         
31238         this.barItems.push(item);
31239         
31240         return item;
31241     },
31242     
31243     getActive : function()
31244     {
31245         var active = false;
31246         
31247         Roo.each(this.barItems, function(v){
31248             
31249             if (!v.isActive()) {
31250                 return;
31251             }
31252             
31253             active = v;
31254             return false;
31255             
31256         });
31257         
31258         return active;
31259     },
31260     
31261     setActiveItem : function(item)
31262     {
31263         var prev = false;
31264         
31265         Roo.each(this.barItems, function(v){
31266             if (v.rid == item.rid) {
31267                 return ;
31268             }
31269             
31270             if (v.isActive()) {
31271                 v.setActive(false);
31272                 prev = v;
31273             }
31274         });
31275
31276         item.setActive(true);
31277         
31278         this.fireEvent('changed', this, item, prev);
31279     },
31280     
31281     getBarItem: function(rid)
31282     {
31283         var ret = false;
31284         
31285         Roo.each(this.barItems, function(e) {
31286             if (e.rid != rid) {
31287                 return;
31288             }
31289             
31290             ret =  e;
31291             return false;
31292         });
31293         
31294         return ret;
31295     },
31296     
31297     indexOfItem : function(item)
31298     {
31299         var index = false;
31300         
31301         Roo.each(this.barItems, function(v, i){
31302             
31303             if (v.rid != item.rid) {
31304                 return;
31305             }
31306             
31307             index = i;
31308             return false
31309         });
31310         
31311         return index;
31312     },
31313     
31314     setActiveNext : function()
31315     {
31316         var i = this.indexOfItem(this.getActive());
31317         
31318         if (i > this.barItems.length) {
31319             return;
31320         }
31321         
31322         this.setActiveItem(this.barItems[i+1]);
31323     },
31324     
31325     setActivePrev : function()
31326     {
31327         var i = this.indexOfItem(this.getActive());
31328         
31329         if (i  < 1) {
31330             return;
31331         }
31332         
31333         this.setActiveItem(this.barItems[i-1]);
31334     },
31335     
31336     format : function()
31337     {
31338         if(!this.barItems.length){
31339             return;
31340         }
31341      
31342         var width = 100 / this.barItems.length;
31343         
31344         Roo.each(this.barItems, function(i){
31345             i.el.setStyle('width', width + '%');
31346             i.topEl.el.setStyle('width', width + '%');
31347             i.bottomEl.el.setStyle('width', width + '%');
31348         }, this);
31349         
31350     }
31351     
31352 });
31353 /*
31354  * - LGPL
31355  *
31356  * Nav Progress Item
31357  * 
31358  */
31359
31360 /**
31361  * @class Roo.bootstrap.NavProgressItem
31362  * @extends Roo.bootstrap.Component
31363  * Bootstrap NavProgressItem class
31364  * @cfg {String} rid the reference id
31365  * @cfg {Boolean} active (true|false) Is item active default false
31366  * @cfg {Boolean} disabled (true|false) Is item active default false
31367  * @cfg {String} html
31368  * @cfg {String} position (top|bottom) text position default bottom
31369  * @cfg {String} icon show icon instead of number
31370  * 
31371  * @constructor
31372  * Create a new NavProgressItem
31373  * @param {Object} config The config object
31374  */
31375 Roo.bootstrap.NavProgressItem = function(config){
31376     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31377     this.addEvents({
31378         // raw events
31379         /**
31380          * @event click
31381          * The raw click event for the entire grid.
31382          * @param {Roo.bootstrap.NavProgressItem} this
31383          * @param {Roo.EventObject} e
31384          */
31385         "click" : true
31386     });
31387    
31388 };
31389
31390 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31391     
31392     rid : '',
31393     active : false,
31394     disabled : false,
31395     html : '',
31396     position : 'bottom',
31397     icon : false,
31398     
31399     getAutoCreate : function()
31400     {
31401         var iconCls = 'roo-navigation-bar-item-icon';
31402         
31403         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31404         
31405         var cfg = {
31406             tag: 'li',
31407             cls: 'roo-navigation-bar-item',
31408             cn : [
31409                 {
31410                     tag : 'i',
31411                     cls : iconCls
31412                 }
31413             ]
31414         };
31415         
31416         if(this.active){
31417             cfg.cls += ' active';
31418         }
31419         if(this.disabled){
31420             cfg.cls += ' disabled';
31421         }
31422         
31423         return cfg;
31424     },
31425     
31426     disable : function()
31427     {
31428         this.setDisabled(true);
31429     },
31430     
31431     enable : function()
31432     {
31433         this.setDisabled(false);
31434     },
31435     
31436     initEvents: function() 
31437     {
31438         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31439         
31440         this.iconEl.on('click', this.onClick, this);
31441     },
31442     
31443     onClick : function(e)
31444     {
31445         e.preventDefault();
31446         
31447         if(this.disabled){
31448             return;
31449         }
31450         
31451         if(this.fireEvent('click', this, e) === false){
31452             return;
31453         };
31454         
31455         this.parent().setActiveItem(this);
31456     },
31457     
31458     isActive: function () 
31459     {
31460         return this.active;
31461     },
31462     
31463     setActive : function(state)
31464     {
31465         if(this.active == state){
31466             return;
31467         }
31468         
31469         this.active = state;
31470         
31471         if (state) {
31472             this.el.addClass('active');
31473             return;
31474         }
31475         
31476         this.el.removeClass('active');
31477         
31478         return;
31479     },
31480     
31481     setDisabled : function(state)
31482     {
31483         if(this.disabled == state){
31484             return;
31485         }
31486         
31487         this.disabled = state;
31488         
31489         if (state) {
31490             this.el.addClass('disabled');
31491             return;
31492         }
31493         
31494         this.el.removeClass('disabled');
31495     },
31496     
31497     tooltipEl : function()
31498     {
31499         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31500     }
31501 });
31502  
31503
31504  /*
31505  * - LGPL
31506  *
31507  * FieldLabel
31508  * 
31509  */
31510
31511 /**
31512  * @class Roo.bootstrap.FieldLabel
31513  * @extends Roo.bootstrap.Component
31514  * Bootstrap FieldLabel class
31515  * @cfg {String} html contents of the element
31516  * @cfg {String} tag tag of the element default label
31517  * @cfg {String} cls class of the element
31518  * @cfg {String} target label target 
31519  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31520  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31521  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31522  * @cfg {String} iconTooltip default "This field is required"
31523  * @cfg {String} indicatorpos (left|right) default left
31524  * 
31525  * @constructor
31526  * Create a new FieldLabel
31527  * @param {Object} config The config object
31528  */
31529
31530 Roo.bootstrap.FieldLabel = function(config){
31531     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31532     
31533     this.addEvents({
31534             /**
31535              * @event invalid
31536              * Fires after the field has been marked as invalid.
31537              * @param {Roo.form.FieldLabel} this
31538              * @param {String} msg The validation message
31539              */
31540             invalid : true,
31541             /**
31542              * @event valid
31543              * Fires after the field has been validated with no errors.
31544              * @param {Roo.form.FieldLabel} this
31545              */
31546             valid : true
31547         });
31548 };
31549
31550 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31551     
31552     tag: 'label',
31553     cls: '',
31554     html: '',
31555     target: '',
31556     allowBlank : true,
31557     invalidClass : 'has-warning',
31558     validClass : 'has-success',
31559     iconTooltip : 'This field is required',
31560     indicatorpos : 'left',
31561     
31562     getAutoCreate : function(){
31563         
31564         var cls = "";
31565         if (!this.allowBlank) {
31566             cls  = "visible";
31567         }
31568         
31569         var cfg = {
31570             tag : this.tag,
31571             cls : 'roo-bootstrap-field-label ' + this.cls,
31572             for : this.target,
31573             cn : [
31574                 {
31575                     tag : 'i',
31576                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31577                     tooltip : this.iconTooltip
31578                 },
31579                 {
31580                     tag : 'span',
31581                     html : this.html
31582                 }
31583             ] 
31584         };
31585         
31586         if(this.indicatorpos == 'right'){
31587             var cfg = {
31588                 tag : this.tag,
31589                 cls : 'roo-bootstrap-field-label ' + this.cls,
31590                 for : this.target,
31591                 cn : [
31592                     {
31593                         tag : 'span',
31594                         html : this.html
31595                     },
31596                     {
31597                         tag : 'i',
31598                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31599                         tooltip : this.iconTooltip
31600                     }
31601                 ] 
31602             };
31603         }
31604         
31605         return cfg;
31606     },
31607     
31608     initEvents: function() 
31609     {
31610         Roo.bootstrap.Element.superclass.initEvents.call(this);
31611         
31612         this.indicator = this.indicatorEl();
31613         
31614         if(this.indicator){
31615             this.indicator.removeClass('visible');
31616             this.indicator.addClass('invisible');
31617         }
31618         
31619         Roo.bootstrap.FieldLabel.register(this);
31620     },
31621     
31622     indicatorEl : function()
31623     {
31624         var indicator = this.el.select('i.roo-required-indicator',true).first();
31625         
31626         if(!indicator){
31627             return false;
31628         }
31629         
31630         return indicator;
31631         
31632     },
31633     
31634     /**
31635      * Mark this field as valid
31636      */
31637     markValid : function()
31638     {
31639         if(this.indicator){
31640             this.indicator.removeClass('visible');
31641             this.indicator.addClass('invisible');
31642         }
31643         if (Roo.bootstrap.version == 3) {
31644             this.el.removeClass(this.invalidClass);
31645             this.el.addClass(this.validClass);
31646         } else {
31647             this.el.removeClass('is-invalid');
31648             this.el.addClass('is-valid');
31649         }
31650         
31651         
31652         this.fireEvent('valid', this);
31653     },
31654     
31655     /**
31656      * Mark this field as invalid
31657      * @param {String} msg The validation message
31658      */
31659     markInvalid : function(msg)
31660     {
31661         if(this.indicator){
31662             this.indicator.removeClass('invisible');
31663             this.indicator.addClass('visible');
31664         }
31665           if (Roo.bootstrap.version == 3) {
31666             this.el.removeClass(this.validClass);
31667             this.el.addClass(this.invalidClass);
31668         } else {
31669             this.el.removeClass('is-valid');
31670             this.el.addClass('is-invalid');
31671         }
31672         
31673         
31674         this.fireEvent('invalid', this, msg);
31675     }
31676     
31677    
31678 });
31679
31680 Roo.apply(Roo.bootstrap.FieldLabel, {
31681     
31682     groups: {},
31683     
31684      /**
31685     * register a FieldLabel Group
31686     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31687     */
31688     register : function(label)
31689     {
31690         if(this.groups.hasOwnProperty(label.target)){
31691             return;
31692         }
31693      
31694         this.groups[label.target] = label;
31695         
31696     },
31697     /**
31698     * fetch a FieldLabel Group based on the target
31699     * @param {string} target
31700     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31701     */
31702     get: function(target) {
31703         if (typeof(this.groups[target]) == 'undefined') {
31704             return false;
31705         }
31706         
31707         return this.groups[target] ;
31708     }
31709 });
31710
31711  
31712
31713  /*
31714  * - LGPL
31715  *
31716  * page DateSplitField.
31717  * 
31718  */
31719
31720
31721 /**
31722  * @class Roo.bootstrap.DateSplitField
31723  * @extends Roo.bootstrap.Component
31724  * Bootstrap DateSplitField class
31725  * @cfg {string} fieldLabel - the label associated
31726  * @cfg {Number} labelWidth set the width of label (0-12)
31727  * @cfg {String} labelAlign (top|left)
31728  * @cfg {Boolean} dayAllowBlank (true|false) default false
31729  * @cfg {Boolean} monthAllowBlank (true|false) default false
31730  * @cfg {Boolean} yearAllowBlank (true|false) default false
31731  * @cfg {string} dayPlaceholder 
31732  * @cfg {string} monthPlaceholder
31733  * @cfg {string} yearPlaceholder
31734  * @cfg {string} dayFormat default 'd'
31735  * @cfg {string} monthFormat default 'm'
31736  * @cfg {string} yearFormat default 'Y'
31737  * @cfg {Number} labellg set the width of label (1-12)
31738  * @cfg {Number} labelmd set the width of label (1-12)
31739  * @cfg {Number} labelsm set the width of label (1-12)
31740  * @cfg {Number} labelxs set the width of label (1-12)
31741
31742  *     
31743  * @constructor
31744  * Create a new DateSplitField
31745  * @param {Object} config The config object
31746  */
31747
31748 Roo.bootstrap.DateSplitField = function(config){
31749     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31750     
31751     this.addEvents({
31752         // raw events
31753          /**
31754          * @event years
31755          * getting the data of years
31756          * @param {Roo.bootstrap.DateSplitField} this
31757          * @param {Object} years
31758          */
31759         "years" : true,
31760         /**
31761          * @event days
31762          * getting the data of days
31763          * @param {Roo.bootstrap.DateSplitField} this
31764          * @param {Object} days
31765          */
31766         "days" : true,
31767         /**
31768          * @event invalid
31769          * Fires after the field has been marked as invalid.
31770          * @param {Roo.form.Field} this
31771          * @param {String} msg The validation message
31772          */
31773         invalid : true,
31774        /**
31775          * @event valid
31776          * Fires after the field has been validated with no errors.
31777          * @param {Roo.form.Field} this
31778          */
31779         valid : true
31780     });
31781 };
31782
31783 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31784     
31785     fieldLabel : '',
31786     labelAlign : 'top',
31787     labelWidth : 3,
31788     dayAllowBlank : false,
31789     monthAllowBlank : false,
31790     yearAllowBlank : false,
31791     dayPlaceholder : '',
31792     monthPlaceholder : '',
31793     yearPlaceholder : '',
31794     dayFormat : 'd',
31795     monthFormat : 'm',
31796     yearFormat : 'Y',
31797     isFormField : true,
31798     labellg : 0,
31799     labelmd : 0,
31800     labelsm : 0,
31801     labelxs : 0,
31802     
31803     getAutoCreate : function()
31804     {
31805         var cfg = {
31806             tag : 'div',
31807             cls : 'row roo-date-split-field-group',
31808             cn : [
31809                 {
31810                     tag : 'input',
31811                     type : 'hidden',
31812                     cls : 'form-hidden-field roo-date-split-field-group-value',
31813                     name : this.name
31814                 }
31815             ]
31816         };
31817         
31818         var labelCls = 'col-md-12';
31819         var contentCls = 'col-md-4';
31820         
31821         if(this.fieldLabel){
31822             
31823             var label = {
31824                 tag : 'div',
31825                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31826                 cn : [
31827                     {
31828                         tag : 'label',
31829                         html : this.fieldLabel
31830                     }
31831                 ]
31832             };
31833             
31834             if(this.labelAlign == 'left'){
31835             
31836                 if(this.labelWidth > 12){
31837                     label.style = "width: " + this.labelWidth + 'px';
31838                 }
31839
31840                 if(this.labelWidth < 13 && this.labelmd == 0){
31841                     this.labelmd = this.labelWidth;
31842                 }
31843
31844                 if(this.labellg > 0){
31845                     labelCls = ' col-lg-' + this.labellg;
31846                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31847                 }
31848
31849                 if(this.labelmd > 0){
31850                     labelCls = ' col-md-' + this.labelmd;
31851                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31852                 }
31853
31854                 if(this.labelsm > 0){
31855                     labelCls = ' col-sm-' + this.labelsm;
31856                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31857                 }
31858
31859                 if(this.labelxs > 0){
31860                     labelCls = ' col-xs-' + this.labelxs;
31861                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31862                 }
31863             }
31864             
31865             label.cls += ' ' + labelCls;
31866             
31867             cfg.cn.push(label);
31868         }
31869         
31870         Roo.each(['day', 'month', 'year'], function(t){
31871             cfg.cn.push({
31872                 tag : 'div',
31873                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31874             });
31875         }, this);
31876         
31877         return cfg;
31878     },
31879     
31880     inputEl: function ()
31881     {
31882         return this.el.select('.roo-date-split-field-group-value', true).first();
31883     },
31884     
31885     onRender : function(ct, position) 
31886     {
31887         var _this = this;
31888         
31889         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31890         
31891         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31892         
31893         this.dayField = new Roo.bootstrap.ComboBox({
31894             allowBlank : this.dayAllowBlank,
31895             alwaysQuery : true,
31896             displayField : 'value',
31897             editable : false,
31898             fieldLabel : '',
31899             forceSelection : true,
31900             mode : 'local',
31901             placeholder : this.dayPlaceholder,
31902             selectOnFocus : true,
31903             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31904             triggerAction : 'all',
31905             typeAhead : true,
31906             valueField : 'value',
31907             store : new Roo.data.SimpleStore({
31908                 data : (function() {    
31909                     var days = [];
31910                     _this.fireEvent('days', _this, days);
31911                     return days;
31912                 })(),
31913                 fields : [ 'value' ]
31914             }),
31915             listeners : {
31916                 select : function (_self, record, index)
31917                 {
31918                     _this.setValue(_this.getValue());
31919                 }
31920             }
31921         });
31922
31923         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31924         
31925         this.monthField = new Roo.bootstrap.MonthField({
31926             after : '<i class=\"fa fa-calendar\"></i>',
31927             allowBlank : this.monthAllowBlank,
31928             placeholder : this.monthPlaceholder,
31929             readOnly : true,
31930             listeners : {
31931                 render : function (_self)
31932                 {
31933                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31934                         e.preventDefault();
31935                         _self.focus();
31936                     });
31937                 },
31938                 select : function (_self, oldvalue, newvalue)
31939                 {
31940                     _this.setValue(_this.getValue());
31941                 }
31942             }
31943         });
31944         
31945         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31946         
31947         this.yearField = new Roo.bootstrap.ComboBox({
31948             allowBlank : this.yearAllowBlank,
31949             alwaysQuery : true,
31950             displayField : 'value',
31951             editable : false,
31952             fieldLabel : '',
31953             forceSelection : true,
31954             mode : 'local',
31955             placeholder : this.yearPlaceholder,
31956             selectOnFocus : true,
31957             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31958             triggerAction : 'all',
31959             typeAhead : true,
31960             valueField : 'value',
31961             store : new Roo.data.SimpleStore({
31962                 data : (function() {
31963                     var years = [];
31964                     _this.fireEvent('years', _this, years);
31965                     return years;
31966                 })(),
31967                 fields : [ 'value' ]
31968             }),
31969             listeners : {
31970                 select : function (_self, record, index)
31971                 {
31972                     _this.setValue(_this.getValue());
31973                 }
31974             }
31975         });
31976
31977         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31978     },
31979     
31980     setValue : function(v, format)
31981     {
31982         this.inputEl.dom.value = v;
31983         
31984         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31985         
31986         var d = Date.parseDate(v, f);
31987         
31988         if(!d){
31989             this.validate();
31990             return;
31991         }
31992         
31993         this.setDay(d.format(this.dayFormat));
31994         this.setMonth(d.format(this.monthFormat));
31995         this.setYear(d.format(this.yearFormat));
31996         
31997         this.validate();
31998         
31999         return;
32000     },
32001     
32002     setDay : function(v)
32003     {
32004         this.dayField.setValue(v);
32005         this.inputEl.dom.value = this.getValue();
32006         this.validate();
32007         return;
32008     },
32009     
32010     setMonth : function(v)
32011     {
32012         this.monthField.setValue(v, true);
32013         this.inputEl.dom.value = this.getValue();
32014         this.validate();
32015         return;
32016     },
32017     
32018     setYear : function(v)
32019     {
32020         this.yearField.setValue(v);
32021         this.inputEl.dom.value = this.getValue();
32022         this.validate();
32023         return;
32024     },
32025     
32026     getDay : function()
32027     {
32028         return this.dayField.getValue();
32029     },
32030     
32031     getMonth : function()
32032     {
32033         return this.monthField.getValue();
32034     },
32035     
32036     getYear : function()
32037     {
32038         return this.yearField.getValue();
32039     },
32040     
32041     getValue : function()
32042     {
32043         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32044         
32045         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32046         
32047         return date;
32048     },
32049     
32050     reset : function()
32051     {
32052         this.setDay('');
32053         this.setMonth('');
32054         this.setYear('');
32055         this.inputEl.dom.value = '';
32056         this.validate();
32057         return;
32058     },
32059     
32060     validate : function()
32061     {
32062         var d = this.dayField.validate();
32063         var m = this.monthField.validate();
32064         var y = this.yearField.validate();
32065         
32066         var valid = true;
32067         
32068         if(
32069                 (!this.dayAllowBlank && !d) ||
32070                 (!this.monthAllowBlank && !m) ||
32071                 (!this.yearAllowBlank && !y)
32072         ){
32073             valid = false;
32074         }
32075         
32076         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32077             return valid;
32078         }
32079         
32080         if(valid){
32081             this.markValid();
32082             return valid;
32083         }
32084         
32085         this.markInvalid();
32086         
32087         return valid;
32088     },
32089     
32090     markValid : function()
32091     {
32092         
32093         var label = this.el.select('label', true).first();
32094         var icon = this.el.select('i.fa-star', true).first();
32095
32096         if(label && icon){
32097             icon.remove();
32098         }
32099         
32100         this.fireEvent('valid', this);
32101     },
32102     
32103      /**
32104      * Mark this field as invalid
32105      * @param {String} msg The validation message
32106      */
32107     markInvalid : function(msg)
32108     {
32109         
32110         var label = this.el.select('label', true).first();
32111         var icon = this.el.select('i.fa-star', true).first();
32112
32113         if(label && !icon){
32114             this.el.select('.roo-date-split-field-label', true).createChild({
32115                 tag : 'i',
32116                 cls : 'text-danger fa fa-lg fa-star',
32117                 tooltip : 'This field is required',
32118                 style : 'margin-right:5px;'
32119             }, label, true);
32120         }
32121         
32122         this.fireEvent('invalid', this, msg);
32123     },
32124     
32125     clearInvalid : function()
32126     {
32127         var label = this.el.select('label', true).first();
32128         var icon = this.el.select('i.fa-star', true).first();
32129
32130         if(label && icon){
32131             icon.remove();
32132         }
32133         
32134         this.fireEvent('valid', this);
32135     },
32136     
32137     getName: function()
32138     {
32139         return this.name;
32140     }
32141     
32142 });
32143
32144  /**
32145  *
32146  * This is based on 
32147  * http://masonry.desandro.com
32148  *
32149  * The idea is to render all the bricks based on vertical width...
32150  *
32151  * The original code extends 'outlayer' - we might need to use that....
32152  * 
32153  */
32154
32155
32156 /**
32157  * @class Roo.bootstrap.LayoutMasonry
32158  * @extends Roo.bootstrap.Component
32159  * Bootstrap Layout Masonry class
32160  * 
32161  * @constructor
32162  * Create a new Element
32163  * @param {Object} config The config object
32164  */
32165
32166 Roo.bootstrap.LayoutMasonry = function(config){
32167     
32168     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32169     
32170     this.bricks = [];
32171     
32172     Roo.bootstrap.LayoutMasonry.register(this);
32173     
32174     this.addEvents({
32175         // raw events
32176         /**
32177          * @event layout
32178          * Fire after layout the items
32179          * @param {Roo.bootstrap.LayoutMasonry} this
32180          * @param {Roo.EventObject} e
32181          */
32182         "layout" : true
32183     });
32184     
32185 };
32186
32187 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32188     
32189     /**
32190      * @cfg {Boolean} isLayoutInstant = no animation?
32191      */   
32192     isLayoutInstant : false, // needed?
32193    
32194     /**
32195      * @cfg {Number} boxWidth  width of the columns
32196      */   
32197     boxWidth : 450,
32198     
32199       /**
32200      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32201      */   
32202     boxHeight : 0,
32203     
32204     /**
32205      * @cfg {Number} padWidth padding below box..
32206      */   
32207     padWidth : 10, 
32208     
32209     /**
32210      * @cfg {Number} gutter gutter width..
32211      */   
32212     gutter : 10,
32213     
32214      /**
32215      * @cfg {Number} maxCols maximum number of columns
32216      */   
32217     
32218     maxCols: 0,
32219     
32220     /**
32221      * @cfg {Boolean} isAutoInitial defalut true
32222      */   
32223     isAutoInitial : true, 
32224     
32225     containerWidth: 0,
32226     
32227     /**
32228      * @cfg {Boolean} isHorizontal defalut false
32229      */   
32230     isHorizontal : false, 
32231
32232     currentSize : null,
32233     
32234     tag: 'div',
32235     
32236     cls: '',
32237     
32238     bricks: null, //CompositeElement
32239     
32240     cols : 1,
32241     
32242     _isLayoutInited : false,
32243     
32244 //    isAlternative : false, // only use for vertical layout...
32245     
32246     /**
32247      * @cfg {Number} alternativePadWidth padding below box..
32248      */   
32249     alternativePadWidth : 50,
32250     
32251     selectedBrick : [],
32252     
32253     getAutoCreate : function(){
32254         
32255         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32256         
32257         var cfg = {
32258             tag: this.tag,
32259             cls: 'blog-masonary-wrapper ' + this.cls,
32260             cn : {
32261                 cls : 'mas-boxes masonary'
32262             }
32263         };
32264         
32265         return cfg;
32266     },
32267     
32268     getChildContainer: function( )
32269     {
32270         if (this.boxesEl) {
32271             return this.boxesEl;
32272         }
32273         
32274         this.boxesEl = this.el.select('.mas-boxes').first();
32275         
32276         return this.boxesEl;
32277     },
32278     
32279     
32280     initEvents : function()
32281     {
32282         var _this = this;
32283         
32284         if(this.isAutoInitial){
32285             Roo.log('hook children rendered');
32286             this.on('childrenrendered', function() {
32287                 Roo.log('children rendered');
32288                 _this.initial();
32289             } ,this);
32290         }
32291     },
32292     
32293     initial : function()
32294     {
32295         this.selectedBrick = [];
32296         
32297         this.currentSize = this.el.getBox(true);
32298         
32299         Roo.EventManager.onWindowResize(this.resize, this); 
32300
32301         if(!this.isAutoInitial){
32302             this.layout();
32303             return;
32304         }
32305         
32306         this.layout();
32307         
32308         return;
32309         //this.layout.defer(500,this);
32310         
32311     },
32312     
32313     resize : function()
32314     {
32315         var cs = this.el.getBox(true);
32316         
32317         if (
32318                 this.currentSize.width == cs.width && 
32319                 this.currentSize.x == cs.x && 
32320                 this.currentSize.height == cs.height && 
32321                 this.currentSize.y == cs.y 
32322         ) {
32323             Roo.log("no change in with or X or Y");
32324             return;
32325         }
32326         
32327         this.currentSize = cs;
32328         
32329         this.layout();
32330         
32331     },
32332     
32333     layout : function()
32334     {   
32335         this._resetLayout();
32336         
32337         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32338         
32339         this.layoutItems( isInstant );
32340       
32341         this._isLayoutInited = true;
32342         
32343         this.fireEvent('layout', this);
32344         
32345     },
32346     
32347     _resetLayout : function()
32348     {
32349         if(this.isHorizontal){
32350             this.horizontalMeasureColumns();
32351             return;
32352         }
32353         
32354         this.verticalMeasureColumns();
32355         
32356     },
32357     
32358     verticalMeasureColumns : function()
32359     {
32360         this.getContainerWidth();
32361         
32362 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32363 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32364 //            return;
32365 //        }
32366         
32367         var boxWidth = this.boxWidth + this.padWidth;
32368         
32369         if(this.containerWidth < this.boxWidth){
32370             boxWidth = this.containerWidth
32371         }
32372         
32373         var containerWidth = this.containerWidth;
32374         
32375         var cols = Math.floor(containerWidth / boxWidth);
32376         
32377         this.cols = Math.max( cols, 1 );
32378         
32379         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32380         
32381         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32382         
32383         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32384         
32385         this.colWidth = boxWidth + avail - this.padWidth;
32386         
32387         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32388         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32389     },
32390     
32391     horizontalMeasureColumns : function()
32392     {
32393         this.getContainerWidth();
32394         
32395         var boxWidth = this.boxWidth;
32396         
32397         if(this.containerWidth < boxWidth){
32398             boxWidth = this.containerWidth;
32399         }
32400         
32401         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32402         
32403         this.el.setHeight(boxWidth);
32404         
32405     },
32406     
32407     getContainerWidth : function()
32408     {
32409         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32410     },
32411     
32412     layoutItems : function( isInstant )
32413     {
32414         Roo.log(this.bricks);
32415         
32416         var items = Roo.apply([], this.bricks);
32417         
32418         if(this.isHorizontal){
32419             this._horizontalLayoutItems( items , isInstant );
32420             return;
32421         }
32422         
32423 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32424 //            this._verticalAlternativeLayoutItems( items , isInstant );
32425 //            return;
32426 //        }
32427         
32428         this._verticalLayoutItems( items , isInstant );
32429         
32430     },
32431     
32432     _verticalLayoutItems : function ( items , isInstant)
32433     {
32434         if ( !items || !items.length ) {
32435             return;
32436         }
32437         
32438         var standard = [
32439             ['xs', 'xs', 'xs', 'tall'],
32440             ['xs', 'xs', 'tall'],
32441             ['xs', 'xs', 'sm'],
32442             ['xs', 'xs', 'xs'],
32443             ['xs', 'tall'],
32444             ['xs', 'sm'],
32445             ['xs', 'xs'],
32446             ['xs'],
32447             
32448             ['sm', 'xs', 'xs'],
32449             ['sm', 'xs'],
32450             ['sm'],
32451             
32452             ['tall', 'xs', 'xs', 'xs'],
32453             ['tall', 'xs', 'xs'],
32454             ['tall', 'xs'],
32455             ['tall']
32456             
32457         ];
32458         
32459         var queue = [];
32460         
32461         var boxes = [];
32462         
32463         var box = [];
32464         
32465         Roo.each(items, function(item, k){
32466             
32467             switch (item.size) {
32468                 // these layouts take up a full box,
32469                 case 'md' :
32470                 case 'md-left' :
32471                 case 'md-right' :
32472                 case 'wide' :
32473                     
32474                     if(box.length){
32475                         boxes.push(box);
32476                         box = [];
32477                     }
32478                     
32479                     boxes.push([item]);
32480                     
32481                     break;
32482                     
32483                 case 'xs' :
32484                 case 'sm' :
32485                 case 'tall' :
32486                     
32487                     box.push(item);
32488                     
32489                     break;
32490                 default :
32491                     break;
32492                     
32493             }
32494             
32495         }, this);
32496         
32497         if(box.length){
32498             boxes.push(box);
32499             box = [];
32500         }
32501         
32502         var filterPattern = function(box, length)
32503         {
32504             if(!box.length){
32505                 return;
32506             }
32507             
32508             var match = false;
32509             
32510             var pattern = box.slice(0, length);
32511             
32512             var format = [];
32513             
32514             Roo.each(pattern, function(i){
32515                 format.push(i.size);
32516             }, this);
32517             
32518             Roo.each(standard, function(s){
32519                 
32520                 if(String(s) != String(format)){
32521                     return;
32522                 }
32523                 
32524                 match = true;
32525                 return false;
32526                 
32527             }, this);
32528             
32529             if(!match && length == 1){
32530                 return;
32531             }
32532             
32533             if(!match){
32534                 filterPattern(box, length - 1);
32535                 return;
32536             }
32537                 
32538             queue.push(pattern);
32539
32540             box = box.slice(length, box.length);
32541
32542             filterPattern(box, 4);
32543
32544             return;
32545             
32546         }
32547         
32548         Roo.each(boxes, function(box, k){
32549             
32550             if(!box.length){
32551                 return;
32552             }
32553             
32554             if(box.length == 1){
32555                 queue.push(box);
32556                 return;
32557             }
32558             
32559             filterPattern(box, 4);
32560             
32561         }, this);
32562         
32563         this._processVerticalLayoutQueue( queue, isInstant );
32564         
32565     },
32566     
32567 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32568 //    {
32569 //        if ( !items || !items.length ) {
32570 //            return;
32571 //        }
32572 //
32573 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32574 //        
32575 //    },
32576     
32577     _horizontalLayoutItems : function ( items , isInstant)
32578     {
32579         if ( !items || !items.length || items.length < 3) {
32580             return;
32581         }
32582         
32583         items.reverse();
32584         
32585         var eItems = items.slice(0, 3);
32586         
32587         items = items.slice(3, items.length);
32588         
32589         var standard = [
32590             ['xs', 'xs', 'xs', 'wide'],
32591             ['xs', 'xs', 'wide'],
32592             ['xs', 'xs', 'sm'],
32593             ['xs', 'xs', 'xs'],
32594             ['xs', 'wide'],
32595             ['xs', 'sm'],
32596             ['xs', 'xs'],
32597             ['xs'],
32598             
32599             ['sm', 'xs', 'xs'],
32600             ['sm', 'xs'],
32601             ['sm'],
32602             
32603             ['wide', 'xs', 'xs', 'xs'],
32604             ['wide', 'xs', 'xs'],
32605             ['wide', 'xs'],
32606             ['wide'],
32607             
32608             ['wide-thin']
32609         ];
32610         
32611         var queue = [];
32612         
32613         var boxes = [];
32614         
32615         var box = [];
32616         
32617         Roo.each(items, function(item, k){
32618             
32619             switch (item.size) {
32620                 case 'md' :
32621                 case 'md-left' :
32622                 case 'md-right' :
32623                 case 'tall' :
32624                     
32625                     if(box.length){
32626                         boxes.push(box);
32627                         box = [];
32628                     }
32629                     
32630                     boxes.push([item]);
32631                     
32632                     break;
32633                     
32634                 case 'xs' :
32635                 case 'sm' :
32636                 case 'wide' :
32637                 case 'wide-thin' :
32638                     
32639                     box.push(item);
32640                     
32641                     break;
32642                 default :
32643                     break;
32644                     
32645             }
32646             
32647         }, this);
32648         
32649         if(box.length){
32650             boxes.push(box);
32651             box = [];
32652         }
32653         
32654         var filterPattern = function(box, length)
32655         {
32656             if(!box.length){
32657                 return;
32658             }
32659             
32660             var match = false;
32661             
32662             var pattern = box.slice(0, length);
32663             
32664             var format = [];
32665             
32666             Roo.each(pattern, function(i){
32667                 format.push(i.size);
32668             }, this);
32669             
32670             Roo.each(standard, function(s){
32671                 
32672                 if(String(s) != String(format)){
32673                     return;
32674                 }
32675                 
32676                 match = true;
32677                 return false;
32678                 
32679             }, this);
32680             
32681             if(!match && length == 1){
32682                 return;
32683             }
32684             
32685             if(!match){
32686                 filterPattern(box, length - 1);
32687                 return;
32688             }
32689                 
32690             queue.push(pattern);
32691
32692             box = box.slice(length, box.length);
32693
32694             filterPattern(box, 4);
32695
32696             return;
32697             
32698         }
32699         
32700         Roo.each(boxes, function(box, k){
32701             
32702             if(!box.length){
32703                 return;
32704             }
32705             
32706             if(box.length == 1){
32707                 queue.push(box);
32708                 return;
32709             }
32710             
32711             filterPattern(box, 4);
32712             
32713         }, this);
32714         
32715         
32716         var prune = [];
32717         
32718         var pos = this.el.getBox(true);
32719         
32720         var minX = pos.x;
32721         
32722         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32723         
32724         var hit_end = false;
32725         
32726         Roo.each(queue, function(box){
32727             
32728             if(hit_end){
32729                 
32730                 Roo.each(box, function(b){
32731                 
32732                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32733                     b.el.hide();
32734
32735                 }, this);
32736
32737                 return;
32738             }
32739             
32740             var mx = 0;
32741             
32742             Roo.each(box, function(b){
32743                 
32744                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32745                 b.el.show();
32746
32747                 mx = Math.max(mx, b.x);
32748                 
32749             }, this);
32750             
32751             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32752             
32753             if(maxX < minX){
32754                 
32755                 Roo.each(box, function(b){
32756                 
32757                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32758                     b.el.hide();
32759                     
32760                 }, this);
32761                 
32762                 hit_end = true;
32763                 
32764                 return;
32765             }
32766             
32767             prune.push(box);
32768             
32769         }, this);
32770         
32771         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32772     },
32773     
32774     /** Sets position of item in DOM
32775     * @param {Element} item
32776     * @param {Number} x - horizontal position
32777     * @param {Number} y - vertical position
32778     * @param {Boolean} isInstant - disables transitions
32779     */
32780     _processVerticalLayoutQueue : function( queue, isInstant )
32781     {
32782         var pos = this.el.getBox(true);
32783         var x = pos.x;
32784         var y = pos.y;
32785         var maxY = [];
32786         
32787         for (var i = 0; i < this.cols; i++){
32788             maxY[i] = pos.y;
32789         }
32790         
32791         Roo.each(queue, function(box, k){
32792             
32793             var col = k % this.cols;
32794             
32795             Roo.each(box, function(b,kk){
32796                 
32797                 b.el.position('absolute');
32798                 
32799                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32800                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32801                 
32802                 if(b.size == 'md-left' || b.size == 'md-right'){
32803                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32804                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32805                 }
32806                 
32807                 b.el.setWidth(width);
32808                 b.el.setHeight(height);
32809                 // iframe?
32810                 b.el.select('iframe',true).setSize(width,height);
32811                 
32812             }, this);
32813             
32814             for (var i = 0; i < this.cols; i++){
32815                 
32816                 if(maxY[i] < maxY[col]){
32817                     col = i;
32818                     continue;
32819                 }
32820                 
32821                 col = Math.min(col, i);
32822                 
32823             }
32824             
32825             x = pos.x + col * (this.colWidth + this.padWidth);
32826             
32827             y = maxY[col];
32828             
32829             var positions = [];
32830             
32831             switch (box.length){
32832                 case 1 :
32833                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32834                     break;
32835                 case 2 :
32836                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32837                     break;
32838                 case 3 :
32839                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32840                     break;
32841                 case 4 :
32842                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32843                     break;
32844                 default :
32845                     break;
32846             }
32847             
32848             Roo.each(box, function(b,kk){
32849                 
32850                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32851                 
32852                 var sz = b.el.getSize();
32853                 
32854                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32855                 
32856             }, this);
32857             
32858         }, this);
32859         
32860         var mY = 0;
32861         
32862         for (var i = 0; i < this.cols; i++){
32863             mY = Math.max(mY, maxY[i]);
32864         }
32865         
32866         this.el.setHeight(mY - pos.y);
32867         
32868     },
32869     
32870 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32871 //    {
32872 //        var pos = this.el.getBox(true);
32873 //        var x = pos.x;
32874 //        var y = pos.y;
32875 //        var maxX = pos.right;
32876 //        
32877 //        var maxHeight = 0;
32878 //        
32879 //        Roo.each(items, function(item, k){
32880 //            
32881 //            var c = k % 2;
32882 //            
32883 //            item.el.position('absolute');
32884 //                
32885 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32886 //
32887 //            item.el.setWidth(width);
32888 //
32889 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32890 //
32891 //            item.el.setHeight(height);
32892 //            
32893 //            if(c == 0){
32894 //                item.el.setXY([x, y], isInstant ? false : true);
32895 //            } else {
32896 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32897 //            }
32898 //            
32899 //            y = y + height + this.alternativePadWidth;
32900 //            
32901 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32902 //            
32903 //        }, this);
32904 //        
32905 //        this.el.setHeight(maxHeight);
32906 //        
32907 //    },
32908     
32909     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32910     {
32911         var pos = this.el.getBox(true);
32912         
32913         var minX = pos.x;
32914         var minY = pos.y;
32915         
32916         var maxX = pos.right;
32917         
32918         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32919         
32920         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32921         
32922         Roo.each(queue, function(box, k){
32923             
32924             Roo.each(box, function(b, kk){
32925                 
32926                 b.el.position('absolute');
32927                 
32928                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32929                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32930                 
32931                 if(b.size == 'md-left' || b.size == 'md-right'){
32932                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32933                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32934                 }
32935                 
32936                 b.el.setWidth(width);
32937                 b.el.setHeight(height);
32938                 
32939             }, this);
32940             
32941             if(!box.length){
32942                 return;
32943             }
32944             
32945             var positions = [];
32946             
32947             switch (box.length){
32948                 case 1 :
32949                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32950                     break;
32951                 case 2 :
32952                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32953                     break;
32954                 case 3 :
32955                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32956                     break;
32957                 case 4 :
32958                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32959                     break;
32960                 default :
32961                     break;
32962             }
32963             
32964             Roo.each(box, function(b,kk){
32965                 
32966                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32967                 
32968                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32969                 
32970             }, this);
32971             
32972         }, this);
32973         
32974     },
32975     
32976     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32977     {
32978         Roo.each(eItems, function(b,k){
32979             
32980             b.size = (k == 0) ? 'sm' : 'xs';
32981             b.x = (k == 0) ? 2 : 1;
32982             b.y = (k == 0) ? 2 : 1;
32983             
32984             b.el.position('absolute');
32985             
32986             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32987                 
32988             b.el.setWidth(width);
32989             
32990             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32991             
32992             b.el.setHeight(height);
32993             
32994         }, this);
32995
32996         var positions = [];
32997         
32998         positions.push({
32999             x : maxX - this.unitWidth * 2 - this.gutter,
33000             y : minY
33001         });
33002         
33003         positions.push({
33004             x : maxX - this.unitWidth,
33005             y : minY + (this.unitWidth + this.gutter) * 2
33006         });
33007         
33008         positions.push({
33009             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33010             y : minY
33011         });
33012         
33013         Roo.each(eItems, function(b,k){
33014             
33015             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33016
33017         }, this);
33018         
33019     },
33020     
33021     getVerticalOneBoxColPositions : function(x, y, box)
33022     {
33023         var pos = [];
33024         
33025         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33026         
33027         if(box[0].size == 'md-left'){
33028             rand = 0;
33029         }
33030         
33031         if(box[0].size == 'md-right'){
33032             rand = 1;
33033         }
33034         
33035         pos.push({
33036             x : x + (this.unitWidth + this.gutter) * rand,
33037             y : y
33038         });
33039         
33040         return pos;
33041     },
33042     
33043     getVerticalTwoBoxColPositions : function(x, y, box)
33044     {
33045         var pos = [];
33046         
33047         if(box[0].size == 'xs'){
33048             
33049             pos.push({
33050                 x : x,
33051                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33052             });
33053
33054             pos.push({
33055                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33056                 y : y
33057             });
33058             
33059             return pos;
33060             
33061         }
33062         
33063         pos.push({
33064             x : x,
33065             y : y
33066         });
33067
33068         pos.push({
33069             x : x + (this.unitWidth + this.gutter) * 2,
33070             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33071         });
33072         
33073         return pos;
33074         
33075     },
33076     
33077     getVerticalThreeBoxColPositions : function(x, y, box)
33078     {
33079         var pos = [];
33080         
33081         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33082             
33083             pos.push({
33084                 x : x,
33085                 y : y
33086             });
33087
33088             pos.push({
33089                 x : x + (this.unitWidth + this.gutter) * 1,
33090                 y : y
33091             });
33092             
33093             pos.push({
33094                 x : x + (this.unitWidth + this.gutter) * 2,
33095                 y : y
33096             });
33097             
33098             return pos;
33099             
33100         }
33101         
33102         if(box[0].size == 'xs' && box[1].size == 'xs'){
33103             
33104             pos.push({
33105                 x : x,
33106                 y : y
33107             });
33108
33109             pos.push({
33110                 x : x,
33111                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33112             });
33113             
33114             pos.push({
33115                 x : x + (this.unitWidth + this.gutter) * 1,
33116                 y : y
33117             });
33118             
33119             return pos;
33120             
33121         }
33122         
33123         pos.push({
33124             x : x,
33125             y : y
33126         });
33127
33128         pos.push({
33129             x : x + (this.unitWidth + this.gutter) * 2,
33130             y : y
33131         });
33132
33133         pos.push({
33134             x : x + (this.unitWidth + this.gutter) * 2,
33135             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33136         });
33137             
33138         return pos;
33139         
33140     },
33141     
33142     getVerticalFourBoxColPositions : function(x, y, box)
33143     {
33144         var pos = [];
33145         
33146         if(box[0].size == 'xs'){
33147             
33148             pos.push({
33149                 x : x,
33150                 y : y
33151             });
33152
33153             pos.push({
33154                 x : x,
33155                 y : y + (this.unitHeight + this.gutter) * 1
33156             });
33157             
33158             pos.push({
33159                 x : x,
33160                 y : y + (this.unitHeight + this.gutter) * 2
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.unitHeightunitWidth + this.gutter) * 2,
33184             y : y + (this.unitHeight + this.gutter) * 1
33185         });
33186
33187         pos.push({
33188             x : x + (this.unitWidth + this.gutter) * 2,
33189             y : y + (this.unitWidth + this.gutter) * 2
33190         });
33191
33192         return pos;
33193         
33194     },
33195     
33196     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33197     {
33198         var pos = [];
33199         
33200         if(box[0].size == 'md-left'){
33201             pos.push({
33202                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33203                 y : minY
33204             });
33205             
33206             return pos;
33207         }
33208         
33209         if(box[0].size == 'md-right'){
33210             pos.push({
33211                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33212                 y : minY + (this.unitWidth + this.gutter) * 1
33213             });
33214             
33215             return pos;
33216         }
33217         
33218         var rand = Math.floor(Math.random() * (4 - box[0].y));
33219         
33220         pos.push({
33221             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33222             y : minY + (this.unitWidth + this.gutter) * rand
33223         });
33224         
33225         return pos;
33226         
33227     },
33228     
33229     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33230     {
33231         var pos = [];
33232         
33233         if(box[0].size == 'xs'){
33234             
33235             pos.push({
33236                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33237                 y : minY
33238             });
33239
33240             pos.push({
33241                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33242                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33243             });
33244             
33245             return pos;
33246             
33247         }
33248         
33249         pos.push({
33250             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33251             y : minY
33252         });
33253
33254         pos.push({
33255             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33256             y : minY + (this.unitWidth + this.gutter) * 2
33257         });
33258         
33259         return pos;
33260         
33261     },
33262     
33263     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33264     {
33265         var pos = [];
33266         
33267         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33268             
33269             pos.push({
33270                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33271                 y : minY
33272             });
33273
33274             pos.push({
33275                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33276                 y : minY + (this.unitWidth + this.gutter) * 1
33277             });
33278             
33279             pos.push({
33280                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33281                 y : minY + (this.unitWidth + this.gutter) * 2
33282             });
33283             
33284             return pos;
33285             
33286         }
33287         
33288         if(box[0].size == 'xs' && box[1].size == 'xs'){
33289             
33290             pos.push({
33291                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33292                 y : minY
33293             });
33294
33295             pos.push({
33296                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33297                 y : minY
33298             });
33299             
33300             pos.push({
33301                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33302                 y : minY + (this.unitWidth + this.gutter) * 1
33303             });
33304             
33305             return pos;
33306             
33307         }
33308         
33309         pos.push({
33310             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33311             y : minY
33312         });
33313
33314         pos.push({
33315             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33316             y : minY + (this.unitWidth + this.gutter) * 2
33317         });
33318
33319         pos.push({
33320             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33321             y : minY + (this.unitWidth + this.gutter) * 2
33322         });
33323             
33324         return pos;
33325         
33326     },
33327     
33328     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33329     {
33330         var pos = [];
33331         
33332         if(box[0].size == 'xs'){
33333             
33334             pos.push({
33335                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33336                 y : minY
33337             });
33338
33339             pos.push({
33340                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33346                 y : minY
33347             });
33348             
33349             pos.push({
33350                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].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         pos.push({
33374             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),
33375             y : minY + (this.unitWidth + this.gutter) * 2
33376         });
33377
33378         return pos;
33379         
33380     },
33381     
33382     /**
33383     * remove a Masonry Brick
33384     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33385     */
33386     removeBrick : function(brick_id)
33387     {
33388         if (!brick_id) {
33389             return;
33390         }
33391         
33392         for (var i = 0; i<this.bricks.length; i++) {
33393             if (this.bricks[i].id == brick_id) {
33394                 this.bricks.splice(i,1);
33395                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33396                 this.initial();
33397             }
33398         }
33399     },
33400     
33401     /**
33402     * adds a Masonry Brick
33403     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33404     */
33405     addBrick : function(cfg)
33406     {
33407         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33408         //this.register(cn);
33409         cn.parentId = this.id;
33410         cn.render(this.el);
33411         return cn;
33412     },
33413     
33414     /**
33415     * register a Masonry Brick
33416     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33417     */
33418     
33419     register : function(brick)
33420     {
33421         this.bricks.push(brick);
33422         brick.masonryId = this.id;
33423     },
33424     
33425     /**
33426     * clear all the Masonry Brick
33427     */
33428     clearAll : function()
33429     {
33430         this.bricks = [];
33431         //this.getChildContainer().dom.innerHTML = "";
33432         this.el.dom.innerHTML = '';
33433     },
33434     
33435     getSelected : function()
33436     {
33437         if (!this.selectedBrick) {
33438             return false;
33439         }
33440         
33441         return this.selectedBrick;
33442     }
33443 });
33444
33445 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33446     
33447     groups: {},
33448      /**
33449     * register a Masonry Layout
33450     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33451     */
33452     
33453     register : function(layout)
33454     {
33455         this.groups[layout.id] = layout;
33456     },
33457     /**
33458     * fetch a  Masonry Layout based on the masonry layout ID
33459     * @param {string} the masonry layout to add
33460     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33461     */
33462     
33463     get: function(layout_id) {
33464         if (typeof(this.groups[layout_id]) == 'undefined') {
33465             return false;
33466         }
33467         return this.groups[layout_id] ;
33468     }
33469     
33470     
33471     
33472 });
33473
33474  
33475
33476  /**
33477  *
33478  * This is based on 
33479  * http://masonry.desandro.com
33480  *
33481  * The idea is to render all the bricks based on vertical width...
33482  *
33483  * The original code extends 'outlayer' - we might need to use that....
33484  * 
33485  */
33486
33487
33488 /**
33489  * @class Roo.bootstrap.LayoutMasonryAuto
33490  * @extends Roo.bootstrap.Component
33491  * Bootstrap Layout Masonry class
33492  * 
33493  * @constructor
33494  * Create a new Element
33495  * @param {Object} config The config object
33496  */
33497
33498 Roo.bootstrap.LayoutMasonryAuto = function(config){
33499     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33500 };
33501
33502 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33503     
33504       /**
33505      * @cfg {Boolean} isFitWidth  - resize the width..
33506      */   
33507     isFitWidth : false,  // options..
33508     /**
33509      * @cfg {Boolean} isOriginLeft = left align?
33510      */   
33511     isOriginLeft : true,
33512     /**
33513      * @cfg {Boolean} isOriginTop = top align?
33514      */   
33515     isOriginTop : false,
33516     /**
33517      * @cfg {Boolean} isLayoutInstant = no animation?
33518      */   
33519     isLayoutInstant : false, // needed?
33520     /**
33521      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33522      */   
33523     isResizingContainer : true,
33524     /**
33525      * @cfg {Number} columnWidth  width of the columns 
33526      */   
33527     
33528     columnWidth : 0,
33529     
33530     /**
33531      * @cfg {Number} maxCols maximum number of columns
33532      */   
33533     
33534     maxCols: 0,
33535     /**
33536      * @cfg {Number} padHeight padding below box..
33537      */   
33538     
33539     padHeight : 10, 
33540     
33541     /**
33542      * @cfg {Boolean} isAutoInitial defalut true
33543      */   
33544     
33545     isAutoInitial : true, 
33546     
33547     // private?
33548     gutter : 0,
33549     
33550     containerWidth: 0,
33551     initialColumnWidth : 0,
33552     currentSize : null,
33553     
33554     colYs : null, // array.
33555     maxY : 0,
33556     padWidth: 10,
33557     
33558     
33559     tag: 'div',
33560     cls: '',
33561     bricks: null, //CompositeElement
33562     cols : 0, // array?
33563     // element : null, // wrapped now this.el
33564     _isLayoutInited : null, 
33565     
33566     
33567     getAutoCreate : function(){
33568         
33569         var cfg = {
33570             tag: this.tag,
33571             cls: 'blog-masonary-wrapper ' + this.cls,
33572             cn : {
33573                 cls : 'mas-boxes masonary'
33574             }
33575         };
33576         
33577         return cfg;
33578     },
33579     
33580     getChildContainer: function( )
33581     {
33582         if (this.boxesEl) {
33583             return this.boxesEl;
33584         }
33585         
33586         this.boxesEl = this.el.select('.mas-boxes').first();
33587         
33588         return this.boxesEl;
33589     },
33590     
33591     
33592     initEvents : function()
33593     {
33594         var _this = this;
33595         
33596         if(this.isAutoInitial){
33597             Roo.log('hook children rendered');
33598             this.on('childrenrendered', function() {
33599                 Roo.log('children rendered');
33600                 _this.initial();
33601             } ,this);
33602         }
33603         
33604     },
33605     
33606     initial : function()
33607     {
33608         this.reloadItems();
33609
33610         this.currentSize = this.el.getBox(true);
33611
33612         /// was window resize... - let's see if this works..
33613         Roo.EventManager.onWindowResize(this.resize, this); 
33614
33615         if(!this.isAutoInitial){
33616             this.layout();
33617             return;
33618         }
33619         
33620         this.layout.defer(500,this);
33621     },
33622     
33623     reloadItems: function()
33624     {
33625         this.bricks = this.el.select('.masonry-brick', true);
33626         
33627         this.bricks.each(function(b) {
33628             //Roo.log(b.getSize());
33629             if (!b.attr('originalwidth')) {
33630                 b.attr('originalwidth',  b.getSize().width);
33631             }
33632             
33633         });
33634         
33635         Roo.log(this.bricks.elements.length);
33636     },
33637     
33638     resize : function()
33639     {
33640         Roo.log('resize');
33641         var cs = this.el.getBox(true);
33642         
33643         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33644             Roo.log("no change in with or X");
33645             return;
33646         }
33647         this.currentSize = cs;
33648         this.layout();
33649     },
33650     
33651     layout : function()
33652     {
33653          Roo.log('layout');
33654         this._resetLayout();
33655         //this._manageStamps();
33656       
33657         // don't animate first layout
33658         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33659         this.layoutItems( isInstant );
33660       
33661         // flag for initalized
33662         this._isLayoutInited = true;
33663     },
33664     
33665     layoutItems : function( isInstant )
33666     {
33667         //var items = this._getItemsForLayout( this.items );
33668         // original code supports filtering layout items.. we just ignore it..
33669         
33670         this._layoutItems( this.bricks , isInstant );
33671       
33672         this._postLayout();
33673     },
33674     _layoutItems : function ( items , isInstant)
33675     {
33676        //this.fireEvent( 'layout', this, items );
33677     
33678
33679         if ( !items || !items.elements.length ) {
33680           // no items, emit event with empty array
33681             return;
33682         }
33683
33684         var queue = [];
33685         items.each(function(item) {
33686             Roo.log("layout item");
33687             Roo.log(item);
33688             // get x/y object from method
33689             var position = this._getItemLayoutPosition( item );
33690             // enqueue
33691             position.item = item;
33692             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33693             queue.push( position );
33694         }, this);
33695       
33696         this._processLayoutQueue( queue );
33697     },
33698     /** Sets position of item in DOM
33699     * @param {Element} item
33700     * @param {Number} x - horizontal position
33701     * @param {Number} y - vertical position
33702     * @param {Boolean} isInstant - disables transitions
33703     */
33704     _processLayoutQueue : function( queue )
33705     {
33706         for ( var i=0, len = queue.length; i < len; i++ ) {
33707             var obj = queue[i];
33708             obj.item.position('absolute');
33709             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33710         }
33711     },
33712       
33713     
33714     /**
33715     * Any logic you want to do after each layout,
33716     * i.e. size the container
33717     */
33718     _postLayout : function()
33719     {
33720         this.resizeContainer();
33721     },
33722     
33723     resizeContainer : function()
33724     {
33725         if ( !this.isResizingContainer ) {
33726             return;
33727         }
33728         var size = this._getContainerSize();
33729         if ( size ) {
33730             this.el.setSize(size.width,size.height);
33731             this.boxesEl.setSize(size.width,size.height);
33732         }
33733     },
33734     
33735     
33736     
33737     _resetLayout : function()
33738     {
33739         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33740         this.colWidth = this.el.getWidth();
33741         //this.gutter = this.el.getWidth(); 
33742         
33743         this.measureColumns();
33744
33745         // reset column Y
33746         var i = this.cols;
33747         this.colYs = [];
33748         while (i--) {
33749             this.colYs.push( 0 );
33750         }
33751     
33752         this.maxY = 0;
33753     },
33754
33755     measureColumns : function()
33756     {
33757         this.getContainerWidth();
33758       // if columnWidth is 0, default to outerWidth of first item
33759         if ( !this.columnWidth ) {
33760             var firstItem = this.bricks.first();
33761             Roo.log(firstItem);
33762             this.columnWidth  = this.containerWidth;
33763             if (firstItem && firstItem.attr('originalwidth') ) {
33764                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33765             }
33766             // columnWidth fall back to item of first element
33767             Roo.log("set column width?");
33768                         this.initialColumnWidth = this.columnWidth  ;
33769
33770             // if first elem has no width, default to size of container
33771             
33772         }
33773         
33774         
33775         if (this.initialColumnWidth) {
33776             this.columnWidth = this.initialColumnWidth;
33777         }
33778         
33779         
33780             
33781         // column width is fixed at the top - however if container width get's smaller we should
33782         // reduce it...
33783         
33784         // this bit calcs how man columns..
33785             
33786         var columnWidth = this.columnWidth += this.gutter;
33787       
33788         // calculate columns
33789         var containerWidth = this.containerWidth + this.gutter;
33790         
33791         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33792         // fix rounding errors, typically with gutters
33793         var excess = columnWidth - containerWidth % columnWidth;
33794         
33795         
33796         // if overshoot is less than a pixel, round up, otherwise floor it
33797         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33798         cols = Math[ mathMethod ]( cols );
33799         this.cols = Math.max( cols, 1 );
33800         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33801         
33802          // padding positioning..
33803         var totalColWidth = this.cols * this.columnWidth;
33804         var padavail = this.containerWidth - totalColWidth;
33805         // so for 2 columns - we need 3 'pads'
33806         
33807         var padNeeded = (1+this.cols) * this.padWidth;
33808         
33809         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33810         
33811         this.columnWidth += padExtra
33812         //this.padWidth = Math.floor(padavail /  ( this.cols));
33813         
33814         // adjust colum width so that padding is fixed??
33815         
33816         // we have 3 columns ... total = width * 3
33817         // we have X left over... that should be used by 
33818         
33819         //if (this.expandC) {
33820             
33821         //}
33822         
33823         
33824         
33825     },
33826     
33827     getContainerWidth : function()
33828     {
33829        /* // container is parent if fit width
33830         var container = this.isFitWidth ? this.element.parentNode : this.element;
33831         // check that this.size and size are there
33832         // IE8 triggers resize on body size change, so they might not be
33833         
33834         var size = getSize( container );  //FIXME
33835         this.containerWidth = size && size.innerWidth; //FIXME
33836         */
33837          
33838         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33839         
33840     },
33841     
33842     _getItemLayoutPosition : function( item )  // what is item?
33843     {
33844         // we resize the item to our columnWidth..
33845       
33846         item.setWidth(this.columnWidth);
33847         item.autoBoxAdjust  = false;
33848         
33849         var sz = item.getSize();
33850  
33851         // how many columns does this brick span
33852         var remainder = this.containerWidth % this.columnWidth;
33853         
33854         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33855         // round if off by 1 pixel, otherwise use ceil
33856         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33857         colSpan = Math.min( colSpan, this.cols );
33858         
33859         // normally this should be '1' as we dont' currently allow multi width columns..
33860         
33861         var colGroup = this._getColGroup( colSpan );
33862         // get the minimum Y value from the columns
33863         var minimumY = Math.min.apply( Math, colGroup );
33864         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33865         
33866         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33867          
33868         // position the brick
33869         var position = {
33870             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33871             y: this.currentSize.y + minimumY + this.padHeight
33872         };
33873         
33874         Roo.log(position);
33875         // apply setHeight to necessary columns
33876         var setHeight = minimumY + sz.height + this.padHeight;
33877         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33878         
33879         var setSpan = this.cols + 1 - colGroup.length;
33880         for ( var i = 0; i < setSpan; i++ ) {
33881           this.colYs[ shortColIndex + i ] = setHeight ;
33882         }
33883       
33884         return position;
33885     },
33886     
33887     /**
33888      * @param {Number} colSpan - number of columns the element spans
33889      * @returns {Array} colGroup
33890      */
33891     _getColGroup : function( colSpan )
33892     {
33893         if ( colSpan < 2 ) {
33894           // if brick spans only one column, use all the column Ys
33895           return this.colYs;
33896         }
33897       
33898         var colGroup = [];
33899         // how many different places could this brick fit horizontally
33900         var groupCount = this.cols + 1 - colSpan;
33901         // for each group potential horizontal position
33902         for ( var i = 0; i < groupCount; i++ ) {
33903           // make an array of colY values for that one group
33904           var groupColYs = this.colYs.slice( i, i + colSpan );
33905           // and get the max value of the array
33906           colGroup[i] = Math.max.apply( Math, groupColYs );
33907         }
33908         return colGroup;
33909     },
33910     /*
33911     _manageStamp : function( stamp )
33912     {
33913         var stampSize =  stamp.getSize();
33914         var offset = stamp.getBox();
33915         // get the columns that this stamp affects
33916         var firstX = this.isOriginLeft ? offset.x : offset.right;
33917         var lastX = firstX + stampSize.width;
33918         var firstCol = Math.floor( firstX / this.columnWidth );
33919         firstCol = Math.max( 0, firstCol );
33920         
33921         var lastCol = Math.floor( lastX / this.columnWidth );
33922         // lastCol should not go over if multiple of columnWidth #425
33923         lastCol -= lastX % this.columnWidth ? 0 : 1;
33924         lastCol = Math.min( this.cols - 1, lastCol );
33925         
33926         // set colYs to bottom of the stamp
33927         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33928             stampSize.height;
33929             
33930         for ( var i = firstCol; i <= lastCol; i++ ) {
33931           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33932         }
33933     },
33934     */
33935     
33936     _getContainerSize : function()
33937     {
33938         this.maxY = Math.max.apply( Math, this.colYs );
33939         var size = {
33940             height: this.maxY
33941         };
33942       
33943         if ( this.isFitWidth ) {
33944             size.width = this._getContainerFitWidth();
33945         }
33946       
33947         return size;
33948     },
33949     
33950     _getContainerFitWidth : function()
33951     {
33952         var unusedCols = 0;
33953         // count unused columns
33954         var i = this.cols;
33955         while ( --i ) {
33956           if ( this.colYs[i] !== 0 ) {
33957             break;
33958           }
33959           unusedCols++;
33960         }
33961         // fit container to columns that have been used
33962         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33963     },
33964     
33965     needsResizeLayout : function()
33966     {
33967         var previousWidth = this.containerWidth;
33968         this.getContainerWidth();
33969         return previousWidth !== this.containerWidth;
33970     }
33971  
33972 });
33973
33974  
33975
33976  /*
33977  * - LGPL
33978  *
33979  * element
33980  * 
33981  */
33982
33983 /**
33984  * @class Roo.bootstrap.MasonryBrick
33985  * @extends Roo.bootstrap.Component
33986  * Bootstrap MasonryBrick class
33987  * 
33988  * @constructor
33989  * Create a new MasonryBrick
33990  * @param {Object} config The config object
33991  */
33992
33993 Roo.bootstrap.MasonryBrick = function(config){
33994     
33995     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33996     
33997     Roo.bootstrap.MasonryBrick.register(this);
33998     
33999     this.addEvents({
34000         // raw events
34001         /**
34002          * @event click
34003          * When a MasonryBrick is clcik
34004          * @param {Roo.bootstrap.MasonryBrick} this
34005          * @param {Roo.EventObject} e
34006          */
34007         "click" : true
34008     });
34009 };
34010
34011 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34012     
34013     /**
34014      * @cfg {String} title
34015      */   
34016     title : '',
34017     /**
34018      * @cfg {String} html
34019      */   
34020     html : '',
34021     /**
34022      * @cfg {String} bgimage
34023      */   
34024     bgimage : '',
34025     /**
34026      * @cfg {String} videourl
34027      */   
34028     videourl : '',
34029     /**
34030      * @cfg {String} cls
34031      */   
34032     cls : '',
34033     /**
34034      * @cfg {String} href
34035      */   
34036     href : '',
34037     /**
34038      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34039      */   
34040     size : 'xs',
34041     
34042     /**
34043      * @cfg {String} placetitle (center|bottom)
34044      */   
34045     placetitle : '',
34046     
34047     /**
34048      * @cfg {Boolean} isFitContainer defalut true
34049      */   
34050     isFitContainer : true, 
34051     
34052     /**
34053      * @cfg {Boolean} preventDefault defalut false
34054      */   
34055     preventDefault : false, 
34056     
34057     /**
34058      * @cfg {Boolean} inverse defalut false
34059      */   
34060     maskInverse : false, 
34061     
34062     getAutoCreate : function()
34063     {
34064         if(!this.isFitContainer){
34065             return this.getSplitAutoCreate();
34066         }
34067         
34068         var cls = 'masonry-brick masonry-brick-full';
34069         
34070         if(this.href.length){
34071             cls += ' masonry-brick-link';
34072         }
34073         
34074         if(this.bgimage.length){
34075             cls += ' masonry-brick-image';
34076         }
34077         
34078         if(this.maskInverse){
34079             cls += ' mask-inverse';
34080         }
34081         
34082         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34083             cls += ' enable-mask';
34084         }
34085         
34086         if(this.size){
34087             cls += ' masonry-' + this.size + '-brick';
34088         }
34089         
34090         if(this.placetitle.length){
34091             
34092             switch (this.placetitle) {
34093                 case 'center' :
34094                     cls += ' masonry-center-title';
34095                     break;
34096                 case 'bottom' :
34097                     cls += ' masonry-bottom-title';
34098                     break;
34099                 default:
34100                     break;
34101             }
34102             
34103         } else {
34104             if(!this.html.length && !this.bgimage.length){
34105                 cls += ' masonry-center-title';
34106             }
34107
34108             if(!this.html.length && this.bgimage.length){
34109                 cls += ' masonry-bottom-title';
34110             }
34111         }
34112         
34113         if(this.cls){
34114             cls += ' ' + this.cls;
34115         }
34116         
34117         var cfg = {
34118             tag: (this.href.length) ? 'a' : 'div',
34119             cls: cls,
34120             cn: [
34121                 {
34122                     tag: 'div',
34123                     cls: 'masonry-brick-mask'
34124                 },
34125                 {
34126                     tag: 'div',
34127                     cls: 'masonry-brick-paragraph',
34128                     cn: []
34129                 }
34130             ]
34131         };
34132         
34133         if(this.href.length){
34134             cfg.href = this.href;
34135         }
34136         
34137         var cn = cfg.cn[1].cn;
34138         
34139         if(this.title.length){
34140             cn.push({
34141                 tag: 'h4',
34142                 cls: 'masonry-brick-title',
34143                 html: this.title
34144             });
34145         }
34146         
34147         if(this.html.length){
34148             cn.push({
34149                 tag: 'p',
34150                 cls: 'masonry-brick-text',
34151                 html: this.html
34152             });
34153         }
34154         
34155         if (!this.title.length && !this.html.length) {
34156             cfg.cn[1].cls += ' hide';
34157         }
34158         
34159         if(this.bgimage.length){
34160             cfg.cn.push({
34161                 tag: 'img',
34162                 cls: 'masonry-brick-image-view',
34163                 src: this.bgimage
34164             });
34165         }
34166         
34167         if(this.videourl.length){
34168             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34169             // youtube support only?
34170             cfg.cn.push({
34171                 tag: 'iframe',
34172                 cls: 'masonry-brick-image-view',
34173                 src: vurl,
34174                 frameborder : 0,
34175                 allowfullscreen : true
34176             });
34177         }
34178         
34179         return cfg;
34180         
34181     },
34182     
34183     getSplitAutoCreate : function()
34184     {
34185         var cls = 'masonry-brick masonry-brick-split';
34186         
34187         if(this.href.length){
34188             cls += ' masonry-brick-link';
34189         }
34190         
34191         if(this.bgimage.length){
34192             cls += ' masonry-brick-image';
34193         }
34194         
34195         if(this.size){
34196             cls += ' masonry-' + this.size + '-brick';
34197         }
34198         
34199         switch (this.placetitle) {
34200             case 'center' :
34201                 cls += ' masonry-center-title';
34202                 break;
34203             case 'bottom' :
34204                 cls += ' masonry-bottom-title';
34205                 break;
34206             default:
34207                 if(!this.bgimage.length){
34208                     cls += ' masonry-center-title';
34209                 }
34210
34211                 if(this.bgimage.length){
34212                     cls += ' masonry-bottom-title';
34213                 }
34214                 break;
34215         }
34216         
34217         if(this.cls){
34218             cls += ' ' + this.cls;
34219         }
34220         
34221         var cfg = {
34222             tag: (this.href.length) ? 'a' : 'div',
34223             cls: cls,
34224             cn: [
34225                 {
34226                     tag: 'div',
34227                     cls: 'masonry-brick-split-head',
34228                     cn: [
34229                         {
34230                             tag: 'div',
34231                             cls: 'masonry-brick-paragraph',
34232                             cn: []
34233                         }
34234                     ]
34235                 },
34236                 {
34237                     tag: 'div',
34238                     cls: 'masonry-brick-split-body',
34239                     cn: []
34240                 }
34241             ]
34242         };
34243         
34244         if(this.href.length){
34245             cfg.href = this.href;
34246         }
34247         
34248         if(this.title.length){
34249             cfg.cn[0].cn[0].cn.push({
34250                 tag: 'h4',
34251                 cls: 'masonry-brick-title',
34252                 html: this.title
34253             });
34254         }
34255         
34256         if(this.html.length){
34257             cfg.cn[1].cn.push({
34258                 tag: 'p',
34259                 cls: 'masonry-brick-text',
34260                 html: this.html
34261             });
34262         }
34263
34264         if(this.bgimage.length){
34265             cfg.cn[0].cn.push({
34266                 tag: 'img',
34267                 cls: 'masonry-brick-image-view',
34268                 src: this.bgimage
34269             });
34270         }
34271         
34272         if(this.videourl.length){
34273             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34274             // youtube support only?
34275             cfg.cn[0].cn.cn.push({
34276                 tag: 'iframe',
34277                 cls: 'masonry-brick-image-view',
34278                 src: vurl,
34279                 frameborder : 0,
34280                 allowfullscreen : true
34281             });
34282         }
34283         
34284         return cfg;
34285     },
34286     
34287     initEvents: function() 
34288     {
34289         switch (this.size) {
34290             case 'xs' :
34291                 this.x = 1;
34292                 this.y = 1;
34293                 break;
34294             case 'sm' :
34295                 this.x = 2;
34296                 this.y = 2;
34297                 break;
34298             case 'md' :
34299             case 'md-left' :
34300             case 'md-right' :
34301                 this.x = 3;
34302                 this.y = 3;
34303                 break;
34304             case 'tall' :
34305                 this.x = 2;
34306                 this.y = 3;
34307                 break;
34308             case 'wide' :
34309                 this.x = 3;
34310                 this.y = 2;
34311                 break;
34312             case 'wide-thin' :
34313                 this.x = 3;
34314                 this.y = 1;
34315                 break;
34316                         
34317             default :
34318                 break;
34319         }
34320         
34321         if(Roo.isTouch){
34322             this.el.on('touchstart', this.onTouchStart, this);
34323             this.el.on('touchmove', this.onTouchMove, this);
34324             this.el.on('touchend', this.onTouchEnd, this);
34325             this.el.on('contextmenu', this.onContextMenu, this);
34326         } else {
34327             this.el.on('mouseenter'  ,this.enter, this);
34328             this.el.on('mouseleave', this.leave, this);
34329             this.el.on('click', this.onClick, this);
34330         }
34331         
34332         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34333             this.parent().bricks.push(this);   
34334         }
34335         
34336     },
34337     
34338     onClick: function(e, el)
34339     {
34340         var time = this.endTimer - this.startTimer;
34341         // Roo.log(e.preventDefault());
34342         if(Roo.isTouch){
34343             if(time > 1000){
34344                 e.preventDefault();
34345                 return;
34346             }
34347         }
34348         
34349         if(!this.preventDefault){
34350             return;
34351         }
34352         
34353         e.preventDefault();
34354         
34355         if (this.activeClass != '') {
34356             this.selectBrick();
34357         }
34358         
34359         this.fireEvent('click', this, e);
34360     },
34361     
34362     enter: function(e, el)
34363     {
34364         e.preventDefault();
34365         
34366         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34367             return;
34368         }
34369         
34370         if(this.bgimage.length && this.html.length){
34371             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34372         }
34373     },
34374     
34375     leave: function(e, el)
34376     {
34377         e.preventDefault();
34378         
34379         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34380             return;
34381         }
34382         
34383         if(this.bgimage.length && this.html.length){
34384             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34385         }
34386     },
34387     
34388     onTouchStart: function(e, el)
34389     {
34390 //        e.preventDefault();
34391         
34392         this.touchmoved = false;
34393         
34394         if(!this.isFitContainer){
34395             return;
34396         }
34397         
34398         if(!this.bgimage.length || !this.html.length){
34399             return;
34400         }
34401         
34402         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34403         
34404         this.timer = new Date().getTime();
34405         
34406     },
34407     
34408     onTouchMove: function(e, el)
34409     {
34410         this.touchmoved = true;
34411     },
34412     
34413     onContextMenu : function(e,el)
34414     {
34415         e.preventDefault();
34416         e.stopPropagation();
34417         return false;
34418     },
34419     
34420     onTouchEnd: function(e, el)
34421     {
34422 //        e.preventDefault();
34423         
34424         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34425         
34426             this.leave(e,el);
34427             
34428             return;
34429         }
34430         
34431         if(!this.bgimage.length || !this.html.length){
34432             
34433             if(this.href.length){
34434                 window.location.href = this.href;
34435             }
34436             
34437             return;
34438         }
34439         
34440         if(!this.isFitContainer){
34441             return;
34442         }
34443         
34444         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34445         
34446         window.location.href = this.href;
34447     },
34448     
34449     //selection on single brick only
34450     selectBrick : function() {
34451         
34452         if (!this.parentId) {
34453             return;
34454         }
34455         
34456         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34457         var index = m.selectedBrick.indexOf(this.id);
34458         
34459         if ( index > -1) {
34460             m.selectedBrick.splice(index,1);
34461             this.el.removeClass(this.activeClass);
34462             return;
34463         }
34464         
34465         for(var i = 0; i < m.selectedBrick.length; i++) {
34466             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34467             b.el.removeClass(b.activeClass);
34468         }
34469         
34470         m.selectedBrick = [];
34471         
34472         m.selectedBrick.push(this.id);
34473         this.el.addClass(this.activeClass);
34474         return;
34475     },
34476     
34477     isSelected : function(){
34478         return this.el.hasClass(this.activeClass);
34479         
34480     }
34481 });
34482
34483 Roo.apply(Roo.bootstrap.MasonryBrick, {
34484     
34485     //groups: {},
34486     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34487      /**
34488     * register a Masonry Brick
34489     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34490     */
34491     
34492     register : function(brick)
34493     {
34494         //this.groups[brick.id] = brick;
34495         this.groups.add(brick.id, brick);
34496     },
34497     /**
34498     * fetch a  masonry brick based on the masonry brick ID
34499     * @param {string} the masonry brick to add
34500     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34501     */
34502     
34503     get: function(brick_id) 
34504     {
34505         // if (typeof(this.groups[brick_id]) == 'undefined') {
34506         //     return false;
34507         // }
34508         // return this.groups[brick_id] ;
34509         
34510         if(this.groups.key(brick_id)) {
34511             return this.groups.key(brick_id);
34512         }
34513         
34514         return false;
34515     }
34516     
34517     
34518     
34519 });
34520
34521  /*
34522  * - LGPL
34523  *
34524  * element
34525  * 
34526  */
34527
34528 /**
34529  * @class Roo.bootstrap.Brick
34530  * @extends Roo.bootstrap.Component
34531  * Bootstrap Brick class
34532  * 
34533  * @constructor
34534  * Create a new Brick
34535  * @param {Object} config The config object
34536  */
34537
34538 Roo.bootstrap.Brick = function(config){
34539     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34540     
34541     this.addEvents({
34542         // raw events
34543         /**
34544          * @event click
34545          * When a Brick is click
34546          * @param {Roo.bootstrap.Brick} this
34547          * @param {Roo.EventObject} e
34548          */
34549         "click" : true
34550     });
34551 };
34552
34553 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34554     
34555     /**
34556      * @cfg {String} title
34557      */   
34558     title : '',
34559     /**
34560      * @cfg {String} html
34561      */   
34562     html : '',
34563     /**
34564      * @cfg {String} bgimage
34565      */   
34566     bgimage : '',
34567     /**
34568      * @cfg {String} cls
34569      */   
34570     cls : '',
34571     /**
34572      * @cfg {String} href
34573      */   
34574     href : '',
34575     /**
34576      * @cfg {String} video
34577      */   
34578     video : '',
34579     /**
34580      * @cfg {Boolean} square
34581      */   
34582     square : true,
34583     
34584     getAutoCreate : function()
34585     {
34586         var cls = 'roo-brick';
34587         
34588         if(this.href.length){
34589             cls += ' roo-brick-link';
34590         }
34591         
34592         if(this.bgimage.length){
34593             cls += ' roo-brick-image';
34594         }
34595         
34596         if(!this.html.length && !this.bgimage.length){
34597             cls += ' roo-brick-center-title';
34598         }
34599         
34600         if(!this.html.length && this.bgimage.length){
34601             cls += ' roo-brick-bottom-title';
34602         }
34603         
34604         if(this.cls){
34605             cls += ' ' + this.cls;
34606         }
34607         
34608         var cfg = {
34609             tag: (this.href.length) ? 'a' : 'div',
34610             cls: cls,
34611             cn: [
34612                 {
34613                     tag: 'div',
34614                     cls: 'roo-brick-paragraph',
34615                     cn: []
34616                 }
34617             ]
34618         };
34619         
34620         if(this.href.length){
34621             cfg.href = this.href;
34622         }
34623         
34624         var cn = cfg.cn[0].cn;
34625         
34626         if(this.title.length){
34627             cn.push({
34628                 tag: 'h4',
34629                 cls: 'roo-brick-title',
34630                 html: this.title
34631             });
34632         }
34633         
34634         if(this.html.length){
34635             cn.push({
34636                 tag: 'p',
34637                 cls: 'roo-brick-text',
34638                 html: this.html
34639             });
34640         } else {
34641             cn.cls += ' hide';
34642         }
34643         
34644         if(this.bgimage.length){
34645             cfg.cn.push({
34646                 tag: 'img',
34647                 cls: 'roo-brick-image-view',
34648                 src: this.bgimage
34649             });
34650         }
34651         
34652         return cfg;
34653     },
34654     
34655     initEvents: function() 
34656     {
34657         if(this.title.length || this.html.length){
34658             this.el.on('mouseenter'  ,this.enter, this);
34659             this.el.on('mouseleave', this.leave, this);
34660         }
34661         
34662         Roo.EventManager.onWindowResize(this.resize, this); 
34663         
34664         if(this.bgimage.length){
34665             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34666             this.imageEl.on('load', this.onImageLoad, this);
34667             return;
34668         }
34669         
34670         this.resize();
34671     },
34672     
34673     onImageLoad : function()
34674     {
34675         this.resize();
34676     },
34677     
34678     resize : function()
34679     {
34680         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34681         
34682         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34683         
34684         if(this.bgimage.length){
34685             var image = this.el.select('.roo-brick-image-view', true).first();
34686             
34687             image.setWidth(paragraph.getWidth());
34688             
34689             if(this.square){
34690                 image.setHeight(paragraph.getWidth());
34691             }
34692             
34693             this.el.setHeight(image.getHeight());
34694             paragraph.setHeight(image.getHeight());
34695             
34696         }
34697         
34698     },
34699     
34700     enter: function(e, el)
34701     {
34702         e.preventDefault();
34703         
34704         if(this.bgimage.length){
34705             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34706             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34707         }
34708     },
34709     
34710     leave: function(e, el)
34711     {
34712         e.preventDefault();
34713         
34714         if(this.bgimage.length){
34715             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34716             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34717         }
34718     }
34719     
34720 });
34721
34722  
34723
34724  /*
34725  * - LGPL
34726  *
34727  * Number field 
34728  */
34729
34730 /**
34731  * @class Roo.bootstrap.NumberField
34732  * @extends Roo.bootstrap.Input
34733  * Bootstrap NumberField class
34734  * 
34735  * 
34736  * 
34737  * 
34738  * @constructor
34739  * Create a new NumberField
34740  * @param {Object} config The config object
34741  */
34742
34743 Roo.bootstrap.NumberField = function(config){
34744     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34745 };
34746
34747 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34748     
34749     /**
34750      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34751      */
34752     allowDecimals : true,
34753     /**
34754      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34755      */
34756     decimalSeparator : ".",
34757     /**
34758      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34759      */
34760     decimalPrecision : 2,
34761     /**
34762      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34763      */
34764     allowNegative : true,
34765     
34766     /**
34767      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34768      */
34769     allowZero: true,
34770     /**
34771      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34772      */
34773     minValue : Number.NEGATIVE_INFINITY,
34774     /**
34775      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34776      */
34777     maxValue : Number.MAX_VALUE,
34778     /**
34779      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34780      */
34781     minText : "The minimum value for this field is {0}",
34782     /**
34783      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34784      */
34785     maxText : "The maximum value for this field is {0}",
34786     /**
34787      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34788      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34789      */
34790     nanText : "{0} is not a valid number",
34791     /**
34792      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34793      */
34794     thousandsDelimiter : false,
34795     /**
34796      * @cfg {String} valueAlign alignment of value
34797      */
34798     valueAlign : "left",
34799
34800     getAutoCreate : function()
34801     {
34802         var hiddenInput = {
34803             tag: 'input',
34804             type: 'hidden',
34805             id: Roo.id(),
34806             cls: 'hidden-number-input'
34807         };
34808         
34809         if (this.name) {
34810             hiddenInput.name = this.name;
34811         }
34812         
34813         this.name = '';
34814         
34815         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34816         
34817         this.name = hiddenInput.name;
34818         
34819         if(cfg.cn.length > 0) {
34820             cfg.cn.push(hiddenInput);
34821         }
34822         
34823         return cfg;
34824     },
34825
34826     // private
34827     initEvents : function()
34828     {   
34829         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34830         
34831         var allowed = "0123456789";
34832         
34833         if(this.allowDecimals){
34834             allowed += this.decimalSeparator;
34835         }
34836         
34837         if(this.allowNegative){
34838             allowed += "-";
34839         }
34840         
34841         if(this.thousandsDelimiter) {
34842             allowed += ",";
34843         }
34844         
34845         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34846         
34847         var keyPress = function(e){
34848             
34849             var k = e.getKey();
34850             
34851             var c = e.getCharCode();
34852             
34853             if(
34854                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34855                     allowed.indexOf(String.fromCharCode(c)) === -1
34856             ){
34857                 e.stopEvent();
34858                 return;
34859             }
34860             
34861             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34862                 return;
34863             }
34864             
34865             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34866                 e.stopEvent();
34867             }
34868         };
34869         
34870         this.el.on("keypress", keyPress, this);
34871     },
34872     
34873     validateValue : function(value)
34874     {
34875         
34876         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34877             return false;
34878         }
34879         
34880         var num = this.parseValue(value);
34881         
34882         if(isNaN(num)){
34883             this.markInvalid(String.format(this.nanText, value));
34884             return false;
34885         }
34886         
34887         if(num < this.minValue){
34888             this.markInvalid(String.format(this.minText, this.minValue));
34889             return false;
34890         }
34891         
34892         if(num > this.maxValue){
34893             this.markInvalid(String.format(this.maxText, this.maxValue));
34894             return false;
34895         }
34896         
34897         return true;
34898     },
34899
34900     getValue : function()
34901     {
34902         var v = this.hiddenEl().getValue();
34903         
34904         return this.fixPrecision(this.parseValue(v));
34905     },
34906
34907     parseValue : function(value)
34908     {
34909         if(this.thousandsDelimiter) {
34910             value += "";
34911             r = new RegExp(",", "g");
34912             value = value.replace(r, "");
34913         }
34914         
34915         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34916         return isNaN(value) ? '' : value;
34917     },
34918
34919     fixPrecision : function(value)
34920     {
34921         if(this.thousandsDelimiter) {
34922             value += "";
34923             r = new RegExp(",", "g");
34924             value = value.replace(r, "");
34925         }
34926         
34927         var nan = isNaN(value);
34928         
34929         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34930             return nan ? '' : value;
34931         }
34932         return parseFloat(value).toFixed(this.decimalPrecision);
34933     },
34934
34935     setValue : function(v)
34936     {
34937         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34938         
34939         this.value = v;
34940         
34941         if(this.rendered){
34942             
34943             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34944             
34945             this.inputEl().dom.value = (v == '') ? '' :
34946                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34947             
34948             if(!this.allowZero && v === '0') {
34949                 this.hiddenEl().dom.value = '';
34950                 this.inputEl().dom.value = '';
34951             }
34952             
34953             this.validate();
34954         }
34955     },
34956
34957     decimalPrecisionFcn : function(v)
34958     {
34959         return Math.floor(v);
34960     },
34961
34962     beforeBlur : function()
34963     {
34964         var v = this.parseValue(this.getRawValue());
34965         
34966         if(v || v === 0 || v === ''){
34967             this.setValue(v);
34968         }
34969     },
34970     
34971     hiddenEl : function()
34972     {
34973         return this.el.select('input.hidden-number-input',true).first();
34974     }
34975     
34976 });
34977
34978  
34979
34980 /*
34981 * Licence: LGPL
34982 */
34983
34984 /**
34985  * @class Roo.bootstrap.DocumentSlider
34986  * @extends Roo.bootstrap.Component
34987  * Bootstrap DocumentSlider class
34988  * 
34989  * @constructor
34990  * Create a new DocumentViewer
34991  * @param {Object} config The config object
34992  */
34993
34994 Roo.bootstrap.DocumentSlider = function(config){
34995     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34996     
34997     this.files = [];
34998     
34999     this.addEvents({
35000         /**
35001          * @event initial
35002          * Fire after initEvent
35003          * @param {Roo.bootstrap.DocumentSlider} this
35004          */
35005         "initial" : true,
35006         /**
35007          * @event update
35008          * Fire after update
35009          * @param {Roo.bootstrap.DocumentSlider} this
35010          */
35011         "update" : true,
35012         /**
35013          * @event click
35014          * Fire after click
35015          * @param {Roo.bootstrap.DocumentSlider} this
35016          */
35017         "click" : true
35018     });
35019 };
35020
35021 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35022     
35023     files : false,
35024     
35025     indicator : 0,
35026     
35027     getAutoCreate : function()
35028     {
35029         var cfg = {
35030             tag : 'div',
35031             cls : 'roo-document-slider',
35032             cn : [
35033                 {
35034                     tag : 'div',
35035                     cls : 'roo-document-slider-header',
35036                     cn : [
35037                         {
35038                             tag : 'div',
35039                             cls : 'roo-document-slider-header-title'
35040                         }
35041                     ]
35042                 },
35043                 {
35044                     tag : 'div',
35045                     cls : 'roo-document-slider-body',
35046                     cn : [
35047                         {
35048                             tag : 'div',
35049                             cls : 'roo-document-slider-prev',
35050                             cn : [
35051                                 {
35052                                     tag : 'i',
35053                                     cls : 'fa fa-chevron-left'
35054                                 }
35055                             ]
35056                         },
35057                         {
35058                             tag : 'div',
35059                             cls : 'roo-document-slider-thumb',
35060                             cn : [
35061                                 {
35062                                     tag : 'img',
35063                                     cls : 'roo-document-slider-image'
35064                                 }
35065                             ]
35066                         },
35067                         {
35068                             tag : 'div',
35069                             cls : 'roo-document-slider-next',
35070                             cn : [
35071                                 {
35072                                     tag : 'i',
35073                                     cls : 'fa fa-chevron-right'
35074                                 }
35075                             ]
35076                         }
35077                     ]
35078                 }
35079             ]
35080         };
35081         
35082         return cfg;
35083     },
35084     
35085     initEvents : function()
35086     {
35087         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35088         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35089         
35090         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35091         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35092         
35093         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35094         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35095         
35096         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35097         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35098         
35099         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35100         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35101         
35102         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35103         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35104         
35105         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35106         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35107         
35108         this.thumbEl.on('click', this.onClick, this);
35109         
35110         this.prevIndicator.on('click', this.prev, this);
35111         
35112         this.nextIndicator.on('click', this.next, this);
35113         
35114     },
35115     
35116     initial : function()
35117     {
35118         if(this.files.length){
35119             this.indicator = 1;
35120             this.update()
35121         }
35122         
35123         this.fireEvent('initial', this);
35124     },
35125     
35126     update : function()
35127     {
35128         this.imageEl.attr('src', this.files[this.indicator - 1]);
35129         
35130         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35131         
35132         this.prevIndicator.show();
35133         
35134         if(this.indicator == 1){
35135             this.prevIndicator.hide();
35136         }
35137         
35138         this.nextIndicator.show();
35139         
35140         if(this.indicator == this.files.length){
35141             this.nextIndicator.hide();
35142         }
35143         
35144         this.thumbEl.scrollTo('top');
35145         
35146         this.fireEvent('update', this);
35147     },
35148     
35149     onClick : function(e)
35150     {
35151         e.preventDefault();
35152         
35153         this.fireEvent('click', this);
35154     },
35155     
35156     prev : function(e)
35157     {
35158         e.preventDefault();
35159         
35160         this.indicator = Math.max(1, this.indicator - 1);
35161         
35162         this.update();
35163     },
35164     
35165     next : function(e)
35166     {
35167         e.preventDefault();
35168         
35169         this.indicator = Math.min(this.files.length, this.indicator + 1);
35170         
35171         this.update();
35172     }
35173 });
35174 /*
35175  * - LGPL
35176  *
35177  * RadioSet
35178  *
35179  *
35180  */
35181
35182 /**
35183  * @class Roo.bootstrap.RadioSet
35184  * @extends Roo.bootstrap.Input
35185  * Bootstrap RadioSet class
35186  * @cfg {String} indicatorpos (left|right) default left
35187  * @cfg {Boolean} inline (true|false) inline the element (default true)
35188  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35189  * @constructor
35190  * Create a new RadioSet
35191  * @param {Object} config The config object
35192  */
35193
35194 Roo.bootstrap.RadioSet = function(config){
35195     
35196     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35197     
35198     this.radioes = [];
35199     
35200     Roo.bootstrap.RadioSet.register(this);
35201     
35202     this.addEvents({
35203         /**
35204         * @event check
35205         * Fires when the element is checked or unchecked.
35206         * @param {Roo.bootstrap.RadioSet} this This radio
35207         * @param {Roo.bootstrap.Radio} item The checked item
35208         */
35209        check : true,
35210        /**
35211         * @event click
35212         * Fires when the element is click.
35213         * @param {Roo.bootstrap.RadioSet} this This radio set
35214         * @param {Roo.bootstrap.Radio} item The checked item
35215         * @param {Roo.EventObject} e The event object
35216         */
35217        click : true
35218     });
35219     
35220 };
35221
35222 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35223
35224     radioes : false,
35225     
35226     inline : true,
35227     
35228     weight : '',
35229     
35230     indicatorpos : 'left',
35231     
35232     getAutoCreate : function()
35233     {
35234         var label = {
35235             tag : 'label',
35236             cls : 'roo-radio-set-label',
35237             cn : [
35238                 {
35239                     tag : 'span',
35240                     html : this.fieldLabel
35241                 }
35242             ]
35243         };
35244         if (Roo.bootstrap.version == 3) {
35245             
35246             
35247             if(this.indicatorpos == 'left'){
35248                 label.cn.unshift({
35249                     tag : 'i',
35250                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35251                     tooltip : 'This field is required'
35252                 });
35253             } else {
35254                 label.cn.push({
35255                     tag : 'i',
35256                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35257                     tooltip : 'This field is required'
35258                 });
35259             }
35260         }
35261         var items = {
35262             tag : 'div',
35263             cls : 'roo-radio-set-items'
35264         };
35265         
35266         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35267         
35268         if (align === 'left' && this.fieldLabel.length) {
35269             
35270             items = {
35271                 cls : "roo-radio-set-right", 
35272                 cn: [
35273                     items
35274                 ]
35275             };
35276             
35277             if(this.labelWidth > 12){
35278                 label.style = "width: " + this.labelWidth + 'px';
35279             }
35280             
35281             if(this.labelWidth < 13 && this.labelmd == 0){
35282                 this.labelmd = this.labelWidth;
35283             }
35284             
35285             if(this.labellg > 0){
35286                 label.cls += ' col-lg-' + this.labellg;
35287                 items.cls += ' col-lg-' + (12 - this.labellg);
35288             }
35289             
35290             if(this.labelmd > 0){
35291                 label.cls += ' col-md-' + this.labelmd;
35292                 items.cls += ' col-md-' + (12 - this.labelmd);
35293             }
35294             
35295             if(this.labelsm > 0){
35296                 label.cls += ' col-sm-' + this.labelsm;
35297                 items.cls += ' col-sm-' + (12 - this.labelsm);
35298             }
35299             
35300             if(this.labelxs > 0){
35301                 label.cls += ' col-xs-' + this.labelxs;
35302                 items.cls += ' col-xs-' + (12 - this.labelxs);
35303             }
35304         }
35305         
35306         var cfg = {
35307             tag : 'div',
35308             cls : 'roo-radio-set',
35309             cn : [
35310                 {
35311                     tag : 'input',
35312                     cls : 'roo-radio-set-input',
35313                     type : 'hidden',
35314                     name : this.name,
35315                     value : this.value ? this.value :  ''
35316                 },
35317                 label,
35318                 items
35319             ]
35320         };
35321         
35322         if(this.weight.length){
35323             cfg.cls += ' roo-radio-' + this.weight;
35324         }
35325         
35326         if(this.inline) {
35327             cfg.cls += ' roo-radio-set-inline';
35328         }
35329         
35330         var settings=this;
35331         ['xs','sm','md','lg'].map(function(size){
35332             if (settings[size]) {
35333                 cfg.cls += ' col-' + size + '-' + settings[size];
35334             }
35335         });
35336         
35337         return cfg;
35338         
35339     },
35340
35341     initEvents : function()
35342     {
35343         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35344         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35345         
35346         if(!this.fieldLabel.length){
35347             this.labelEl.hide();
35348         }
35349         
35350         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35351         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35352         
35353         this.indicator = this.indicatorEl();
35354         
35355         if(this.indicator){
35356             this.indicator.addClass('invisible');
35357         }
35358         
35359         this.originalValue = this.getValue();
35360         
35361     },
35362     
35363     inputEl: function ()
35364     {
35365         return this.el.select('.roo-radio-set-input', true).first();
35366     },
35367     
35368     getChildContainer : function()
35369     {
35370         return this.itemsEl;
35371     },
35372     
35373     register : function(item)
35374     {
35375         this.radioes.push(item);
35376         
35377     },
35378     
35379     validate : function()
35380     {   
35381         if(this.getVisibilityEl().hasClass('hidden')){
35382             return true;
35383         }
35384         
35385         var valid = false;
35386         
35387         Roo.each(this.radioes, function(i){
35388             if(!i.checked){
35389                 return;
35390             }
35391             
35392             valid = true;
35393             return false;
35394         });
35395         
35396         if(this.allowBlank) {
35397             return true;
35398         }
35399         
35400         if(this.disabled || valid){
35401             this.markValid();
35402             return true;
35403         }
35404         
35405         this.markInvalid();
35406         return false;
35407         
35408     },
35409     
35410     markValid : function()
35411     {
35412         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35413             this.indicatorEl().removeClass('visible');
35414             this.indicatorEl().addClass('invisible');
35415         }
35416         
35417         
35418         if (Roo.bootstrap.version == 3) {
35419             this.el.removeClass([this.invalidClass, this.validClass]);
35420             this.el.addClass(this.validClass);
35421         } else {
35422             this.el.removeClass(['is-invalid','is-valid']);
35423             this.el.addClass(['is-valid']);
35424         }
35425         this.fireEvent('valid', this);
35426     },
35427     
35428     markInvalid : function(msg)
35429     {
35430         if(this.allowBlank || this.disabled){
35431             return;
35432         }
35433         
35434         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35435             this.indicatorEl().removeClass('invisible');
35436             this.indicatorEl().addClass('visible');
35437         }
35438         if (Roo.bootstrap.version == 3) {
35439             this.el.removeClass([this.invalidClass, this.validClass]);
35440             this.el.addClass(this.invalidClass);
35441         } else {
35442             this.el.removeClass(['is-invalid','is-valid']);
35443             this.el.addClass(['is-invalid']);
35444         }
35445         
35446         this.fireEvent('invalid', this, msg);
35447         
35448     },
35449     
35450     setValue : function(v, suppressEvent)
35451     {   
35452         if(this.value === v){
35453             return;
35454         }
35455         
35456         this.value = v;
35457         
35458         if(this.rendered){
35459             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35460         }
35461         
35462         Roo.each(this.radioes, function(i){
35463             i.checked = false;
35464             i.el.removeClass('checked');
35465         });
35466         
35467         Roo.each(this.radioes, function(i){
35468             
35469             if(i.value === v || i.value.toString() === v.toString()){
35470                 i.checked = true;
35471                 i.el.addClass('checked');
35472                 
35473                 if(suppressEvent !== true){
35474                     this.fireEvent('check', this, i);
35475                 }
35476                 
35477                 return false;
35478             }
35479             
35480         }, this);
35481         
35482         this.validate();
35483     },
35484     
35485     clearInvalid : function(){
35486         
35487         if(!this.el || this.preventMark){
35488             return;
35489         }
35490         
35491         this.el.removeClass([this.invalidClass]);
35492         
35493         this.fireEvent('valid', this);
35494     }
35495     
35496 });
35497
35498 Roo.apply(Roo.bootstrap.RadioSet, {
35499     
35500     groups: {},
35501     
35502     register : function(set)
35503     {
35504         this.groups[set.name] = set;
35505     },
35506     
35507     get: function(name) 
35508     {
35509         if (typeof(this.groups[name]) == 'undefined') {
35510             return false;
35511         }
35512         
35513         return this.groups[name] ;
35514     }
35515     
35516 });
35517 /*
35518  * Based on:
35519  * Ext JS Library 1.1.1
35520  * Copyright(c) 2006-2007, Ext JS, LLC.
35521  *
35522  * Originally Released Under LGPL - original licence link has changed is not relivant.
35523  *
35524  * Fork - LGPL
35525  * <script type="text/javascript">
35526  */
35527
35528
35529 /**
35530  * @class Roo.bootstrap.SplitBar
35531  * @extends Roo.util.Observable
35532  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35533  * <br><br>
35534  * Usage:
35535  * <pre><code>
35536 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35537                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35538 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35539 split.minSize = 100;
35540 split.maxSize = 600;
35541 split.animate = true;
35542 split.on('moved', splitterMoved);
35543 </code></pre>
35544  * @constructor
35545  * Create a new SplitBar
35546  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35547  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35548  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35549  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35550                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35551                         position of the SplitBar).
35552  */
35553 Roo.bootstrap.SplitBar = function(cfg){
35554     
35555     /** @private */
35556     
35557     //{
35558     //  dragElement : elm
35559     //  resizingElement: el,
35560         // optional..
35561     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35562     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35563         // existingProxy ???
35564     //}
35565     
35566     this.el = Roo.get(cfg.dragElement, true);
35567     this.el.dom.unselectable = "on";
35568     /** @private */
35569     this.resizingEl = Roo.get(cfg.resizingElement, true);
35570
35571     /**
35572      * @private
35573      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35574      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35575      * @type Number
35576      */
35577     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35578     
35579     /**
35580      * The minimum size of the resizing element. (Defaults to 0)
35581      * @type Number
35582      */
35583     this.minSize = 0;
35584     
35585     /**
35586      * The maximum size of the resizing element. (Defaults to 2000)
35587      * @type Number
35588      */
35589     this.maxSize = 2000;
35590     
35591     /**
35592      * Whether to animate the transition to the new size
35593      * @type Boolean
35594      */
35595     this.animate = false;
35596     
35597     /**
35598      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35599      * @type Boolean
35600      */
35601     this.useShim = false;
35602     
35603     /** @private */
35604     this.shim = null;
35605     
35606     if(!cfg.existingProxy){
35607         /** @private */
35608         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35609     }else{
35610         this.proxy = Roo.get(cfg.existingProxy).dom;
35611     }
35612     /** @private */
35613     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35614     
35615     /** @private */
35616     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35617     
35618     /** @private */
35619     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35620     
35621     /** @private */
35622     this.dragSpecs = {};
35623     
35624     /**
35625      * @private The adapter to use to positon and resize elements
35626      */
35627     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35628     this.adapter.init(this);
35629     
35630     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35631         /** @private */
35632         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35633         this.el.addClass("roo-splitbar-h");
35634     }else{
35635         /** @private */
35636         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35637         this.el.addClass("roo-splitbar-v");
35638     }
35639     
35640     this.addEvents({
35641         /**
35642          * @event resize
35643          * Fires when the splitter is moved (alias for {@link #event-moved})
35644          * @param {Roo.bootstrap.SplitBar} this
35645          * @param {Number} newSize the new width or height
35646          */
35647         "resize" : true,
35648         /**
35649          * @event moved
35650          * Fires when the splitter is moved
35651          * @param {Roo.bootstrap.SplitBar} this
35652          * @param {Number} newSize the new width or height
35653          */
35654         "moved" : true,
35655         /**
35656          * @event beforeresize
35657          * Fires before the splitter is dragged
35658          * @param {Roo.bootstrap.SplitBar} this
35659          */
35660         "beforeresize" : true,
35661
35662         "beforeapply" : true
35663     });
35664
35665     Roo.util.Observable.call(this);
35666 };
35667
35668 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35669     onStartProxyDrag : function(x, y){
35670         this.fireEvent("beforeresize", this);
35671         if(!this.overlay){
35672             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35673             o.unselectable();
35674             o.enableDisplayMode("block");
35675             // all splitbars share the same overlay
35676             Roo.bootstrap.SplitBar.prototype.overlay = o;
35677         }
35678         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35679         this.overlay.show();
35680         Roo.get(this.proxy).setDisplayed("block");
35681         var size = this.adapter.getElementSize(this);
35682         this.activeMinSize = this.getMinimumSize();;
35683         this.activeMaxSize = this.getMaximumSize();;
35684         var c1 = size - this.activeMinSize;
35685         var c2 = Math.max(this.activeMaxSize - size, 0);
35686         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35687             this.dd.resetConstraints();
35688             this.dd.setXConstraint(
35689                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35690                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35691             );
35692             this.dd.setYConstraint(0, 0);
35693         }else{
35694             this.dd.resetConstraints();
35695             this.dd.setXConstraint(0, 0);
35696             this.dd.setYConstraint(
35697                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35698                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35699             );
35700          }
35701         this.dragSpecs.startSize = size;
35702         this.dragSpecs.startPoint = [x, y];
35703         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35704     },
35705     
35706     /** 
35707      * @private Called after the drag operation by the DDProxy
35708      */
35709     onEndProxyDrag : function(e){
35710         Roo.get(this.proxy).setDisplayed(false);
35711         var endPoint = Roo.lib.Event.getXY(e);
35712         if(this.overlay){
35713             this.overlay.hide();
35714         }
35715         var newSize;
35716         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35717             newSize = this.dragSpecs.startSize + 
35718                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35719                     endPoint[0] - this.dragSpecs.startPoint[0] :
35720                     this.dragSpecs.startPoint[0] - endPoint[0]
35721                 );
35722         }else{
35723             newSize = this.dragSpecs.startSize + 
35724                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35725                     endPoint[1] - this.dragSpecs.startPoint[1] :
35726                     this.dragSpecs.startPoint[1] - endPoint[1]
35727                 );
35728         }
35729         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35730         if(newSize != this.dragSpecs.startSize){
35731             if(this.fireEvent('beforeapply', this, newSize) !== false){
35732                 this.adapter.setElementSize(this, newSize);
35733                 this.fireEvent("moved", this, newSize);
35734                 this.fireEvent("resize", this, newSize);
35735             }
35736         }
35737     },
35738     
35739     /**
35740      * Get the adapter this SplitBar uses
35741      * @return The adapter object
35742      */
35743     getAdapter : function(){
35744         return this.adapter;
35745     },
35746     
35747     /**
35748      * Set the adapter this SplitBar uses
35749      * @param {Object} adapter A SplitBar adapter object
35750      */
35751     setAdapter : function(adapter){
35752         this.adapter = adapter;
35753         this.adapter.init(this);
35754     },
35755     
35756     /**
35757      * Gets the minimum size for the resizing element
35758      * @return {Number} The minimum size
35759      */
35760     getMinimumSize : function(){
35761         return this.minSize;
35762     },
35763     
35764     /**
35765      * Sets the minimum size for the resizing element
35766      * @param {Number} minSize The minimum size
35767      */
35768     setMinimumSize : function(minSize){
35769         this.minSize = minSize;
35770     },
35771     
35772     /**
35773      * Gets the maximum size for the resizing element
35774      * @return {Number} The maximum size
35775      */
35776     getMaximumSize : function(){
35777         return this.maxSize;
35778     },
35779     
35780     /**
35781      * Sets the maximum size for the resizing element
35782      * @param {Number} maxSize The maximum size
35783      */
35784     setMaximumSize : function(maxSize){
35785         this.maxSize = maxSize;
35786     },
35787     
35788     /**
35789      * Sets the initialize size for the resizing element
35790      * @param {Number} size The initial size
35791      */
35792     setCurrentSize : function(size){
35793         var oldAnimate = this.animate;
35794         this.animate = false;
35795         this.adapter.setElementSize(this, size);
35796         this.animate = oldAnimate;
35797     },
35798     
35799     /**
35800      * Destroy this splitbar. 
35801      * @param {Boolean} removeEl True to remove the element
35802      */
35803     destroy : function(removeEl){
35804         if(this.shim){
35805             this.shim.remove();
35806         }
35807         this.dd.unreg();
35808         this.proxy.parentNode.removeChild(this.proxy);
35809         if(removeEl){
35810             this.el.remove();
35811         }
35812     }
35813 });
35814
35815 /**
35816  * @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.
35817  */
35818 Roo.bootstrap.SplitBar.createProxy = function(dir){
35819     var proxy = new Roo.Element(document.createElement("div"));
35820     proxy.unselectable();
35821     var cls = 'roo-splitbar-proxy';
35822     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35823     document.body.appendChild(proxy.dom);
35824     return proxy.dom;
35825 };
35826
35827 /** 
35828  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35829  * Default Adapter. It assumes the splitter and resizing element are not positioned
35830  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35831  */
35832 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35833 };
35834
35835 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35836     // do nothing for now
35837     init : function(s){
35838     
35839     },
35840     /**
35841      * Called before drag operations to get the current size of the resizing element. 
35842      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35843      */
35844      getElementSize : function(s){
35845         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35846             return s.resizingEl.getWidth();
35847         }else{
35848             return s.resizingEl.getHeight();
35849         }
35850     },
35851     
35852     /**
35853      * Called after drag operations to set the size of the resizing element.
35854      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35855      * @param {Number} newSize The new size to set
35856      * @param {Function} onComplete A function to be invoked when resizing is complete
35857      */
35858     setElementSize : function(s, newSize, onComplete){
35859         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35860             if(!s.animate){
35861                 s.resizingEl.setWidth(newSize);
35862                 if(onComplete){
35863                     onComplete(s, newSize);
35864                 }
35865             }else{
35866                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35867             }
35868         }else{
35869             
35870             if(!s.animate){
35871                 s.resizingEl.setHeight(newSize);
35872                 if(onComplete){
35873                     onComplete(s, newSize);
35874                 }
35875             }else{
35876                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35877             }
35878         }
35879     }
35880 };
35881
35882 /** 
35883  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35884  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35885  * Adapter that  moves the splitter element to align with the resized sizing element. 
35886  * Used with an absolute positioned SplitBar.
35887  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35888  * document.body, make sure you assign an id to the body element.
35889  */
35890 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35891     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35892     this.container = Roo.get(container);
35893 };
35894
35895 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35896     init : function(s){
35897         this.basic.init(s);
35898     },
35899     
35900     getElementSize : function(s){
35901         return this.basic.getElementSize(s);
35902     },
35903     
35904     setElementSize : function(s, newSize, onComplete){
35905         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35906     },
35907     
35908     moveSplitter : function(s){
35909         var yes = Roo.bootstrap.SplitBar;
35910         switch(s.placement){
35911             case yes.LEFT:
35912                 s.el.setX(s.resizingEl.getRight());
35913                 break;
35914             case yes.RIGHT:
35915                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35916                 break;
35917             case yes.TOP:
35918                 s.el.setY(s.resizingEl.getBottom());
35919                 break;
35920             case yes.BOTTOM:
35921                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35922                 break;
35923         }
35924     }
35925 };
35926
35927 /**
35928  * Orientation constant - Create a vertical SplitBar
35929  * @static
35930  * @type Number
35931  */
35932 Roo.bootstrap.SplitBar.VERTICAL = 1;
35933
35934 /**
35935  * Orientation constant - Create a horizontal SplitBar
35936  * @static
35937  * @type Number
35938  */
35939 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35940
35941 /**
35942  * Placement constant - The resizing element is to the left of the splitter element
35943  * @static
35944  * @type Number
35945  */
35946 Roo.bootstrap.SplitBar.LEFT = 1;
35947
35948 /**
35949  * Placement constant - The resizing element is to the right of the splitter element
35950  * @static
35951  * @type Number
35952  */
35953 Roo.bootstrap.SplitBar.RIGHT = 2;
35954
35955 /**
35956  * Placement constant - The resizing element is positioned above the splitter element
35957  * @static
35958  * @type Number
35959  */
35960 Roo.bootstrap.SplitBar.TOP = 3;
35961
35962 /**
35963  * Placement constant - The resizing element is positioned under splitter element
35964  * @static
35965  * @type Number
35966  */
35967 Roo.bootstrap.SplitBar.BOTTOM = 4;
35968 Roo.namespace("Roo.bootstrap.layout");/*
35969  * Based on:
35970  * Ext JS Library 1.1.1
35971  * Copyright(c) 2006-2007, Ext JS, LLC.
35972  *
35973  * Originally Released Under LGPL - original licence link has changed is not relivant.
35974  *
35975  * Fork - LGPL
35976  * <script type="text/javascript">
35977  */
35978
35979 /**
35980  * @class Roo.bootstrap.layout.Manager
35981  * @extends Roo.bootstrap.Component
35982  * Base class for layout managers.
35983  */
35984 Roo.bootstrap.layout.Manager = function(config)
35985 {
35986     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35987
35988
35989
35990
35991
35992     /** false to disable window resize monitoring @type Boolean */
35993     this.monitorWindowResize = true;
35994     this.regions = {};
35995     this.addEvents({
35996         /**
35997          * @event layout
35998          * Fires when a layout is performed.
35999          * @param {Roo.LayoutManager} this
36000          */
36001         "layout" : true,
36002         /**
36003          * @event regionresized
36004          * Fires when the user resizes a region.
36005          * @param {Roo.LayoutRegion} region The resized region
36006          * @param {Number} newSize The new size (width for east/west, height for north/south)
36007          */
36008         "regionresized" : true,
36009         /**
36010          * @event regioncollapsed
36011          * Fires when a region is collapsed.
36012          * @param {Roo.LayoutRegion} region The collapsed region
36013          */
36014         "regioncollapsed" : true,
36015         /**
36016          * @event regionexpanded
36017          * Fires when a region is expanded.
36018          * @param {Roo.LayoutRegion} region The expanded region
36019          */
36020         "regionexpanded" : true
36021     });
36022     this.updating = false;
36023
36024     if (config.el) {
36025         this.el = Roo.get(config.el);
36026         this.initEvents();
36027     }
36028
36029 };
36030
36031 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36032
36033
36034     regions : null,
36035
36036     monitorWindowResize : true,
36037
36038
36039     updating : false,
36040
36041
36042     onRender : function(ct, position)
36043     {
36044         if(!this.el){
36045             this.el = Roo.get(ct);
36046             this.initEvents();
36047         }
36048         //this.fireEvent('render',this);
36049     },
36050
36051
36052     initEvents: function()
36053     {
36054
36055
36056         // ie scrollbar fix
36057         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36058             document.body.scroll = "no";
36059         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36060             this.el.position('relative');
36061         }
36062         this.id = this.el.id;
36063         this.el.addClass("roo-layout-container");
36064         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36065         if(this.el.dom != document.body ) {
36066             this.el.on('resize', this.layout,this);
36067             this.el.on('show', this.layout,this);
36068         }
36069
36070     },
36071
36072     /**
36073      * Returns true if this layout is currently being updated
36074      * @return {Boolean}
36075      */
36076     isUpdating : function(){
36077         return this.updating;
36078     },
36079
36080     /**
36081      * Suspend the LayoutManager from doing auto-layouts while
36082      * making multiple add or remove calls
36083      */
36084     beginUpdate : function(){
36085         this.updating = true;
36086     },
36087
36088     /**
36089      * Restore auto-layouts and optionally disable the manager from performing a layout
36090      * @param {Boolean} noLayout true to disable a layout update
36091      */
36092     endUpdate : function(noLayout){
36093         this.updating = false;
36094         if(!noLayout){
36095             this.layout();
36096         }
36097     },
36098
36099     layout: function(){
36100         // abstract...
36101     },
36102
36103     onRegionResized : function(region, newSize){
36104         this.fireEvent("regionresized", region, newSize);
36105         this.layout();
36106     },
36107
36108     onRegionCollapsed : function(region){
36109         this.fireEvent("regioncollapsed", region);
36110     },
36111
36112     onRegionExpanded : function(region){
36113         this.fireEvent("regionexpanded", region);
36114     },
36115
36116     /**
36117      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36118      * performs box-model adjustments.
36119      * @return {Object} The size as an object {width: (the width), height: (the height)}
36120      */
36121     getViewSize : function()
36122     {
36123         var size;
36124         if(this.el.dom != document.body){
36125             size = this.el.getSize();
36126         }else{
36127             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36128         }
36129         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36130         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36131         return size;
36132     },
36133
36134     /**
36135      * Returns the Element this layout is bound to.
36136      * @return {Roo.Element}
36137      */
36138     getEl : function(){
36139         return this.el;
36140     },
36141
36142     /**
36143      * Returns the specified region.
36144      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36145      * @return {Roo.LayoutRegion}
36146      */
36147     getRegion : function(target){
36148         return this.regions[target.toLowerCase()];
36149     },
36150
36151     onWindowResize : function(){
36152         if(this.monitorWindowResize){
36153             this.layout();
36154         }
36155     }
36156 });
36157 /*
36158  * Based on:
36159  * Ext JS Library 1.1.1
36160  * Copyright(c) 2006-2007, Ext JS, LLC.
36161  *
36162  * Originally Released Under LGPL - original licence link has changed is not relivant.
36163  *
36164  * Fork - LGPL
36165  * <script type="text/javascript">
36166  */
36167 /**
36168  * @class Roo.bootstrap.layout.Border
36169  * @extends Roo.bootstrap.layout.Manager
36170  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36171  * please see: examples/bootstrap/nested.html<br><br>
36172  
36173 <b>The container the layout is rendered into can be either the body element or any other element.
36174 If it is not the body element, the container needs to either be an absolute positioned element,
36175 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36176 the container size if it is not the body element.</b>
36177
36178 * @constructor
36179 * Create a new Border
36180 * @param {Object} config Configuration options
36181  */
36182 Roo.bootstrap.layout.Border = function(config){
36183     config = config || {};
36184     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36185     
36186     
36187     
36188     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36189         if(config[region]){
36190             config[region].region = region;
36191             this.addRegion(config[region]);
36192         }
36193     },this);
36194     
36195 };
36196
36197 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36198
36199 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36200     
36201     parent : false, // this might point to a 'nest' or a ???
36202     
36203     /**
36204      * Creates and adds a new region if it doesn't already exist.
36205      * @param {String} target The target region key (north, south, east, west or center).
36206      * @param {Object} config The regions config object
36207      * @return {BorderLayoutRegion} The new region
36208      */
36209     addRegion : function(config)
36210     {
36211         if(!this.regions[config.region]){
36212             var r = this.factory(config);
36213             this.bindRegion(r);
36214         }
36215         return this.regions[config.region];
36216     },
36217
36218     // private (kinda)
36219     bindRegion : function(r){
36220         this.regions[r.config.region] = r;
36221         
36222         r.on("visibilitychange",    this.layout, this);
36223         r.on("paneladded",          this.layout, this);
36224         r.on("panelremoved",        this.layout, this);
36225         r.on("invalidated",         this.layout, this);
36226         r.on("resized",             this.onRegionResized, this);
36227         r.on("collapsed",           this.onRegionCollapsed, this);
36228         r.on("expanded",            this.onRegionExpanded, this);
36229     },
36230
36231     /**
36232      * Performs a layout update.
36233      */
36234     layout : function()
36235     {
36236         if(this.updating) {
36237             return;
36238         }
36239         
36240         // render all the rebions if they have not been done alreayd?
36241         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36242             if(this.regions[region] && !this.regions[region].bodyEl){
36243                 this.regions[region].onRender(this.el)
36244             }
36245         },this);
36246         
36247         var size = this.getViewSize();
36248         var w = size.width;
36249         var h = size.height;
36250         var centerW = w;
36251         var centerH = h;
36252         var centerY = 0;
36253         var centerX = 0;
36254         //var x = 0, y = 0;
36255
36256         var rs = this.regions;
36257         var north = rs["north"];
36258         var south = rs["south"]; 
36259         var west = rs["west"];
36260         var east = rs["east"];
36261         var center = rs["center"];
36262         //if(this.hideOnLayout){ // not supported anymore
36263             //c.el.setStyle("display", "none");
36264         //}
36265         if(north && north.isVisible()){
36266             var b = north.getBox();
36267             var m = north.getMargins();
36268             b.width = w - (m.left+m.right);
36269             b.x = m.left;
36270             b.y = m.top;
36271             centerY = b.height + b.y + m.bottom;
36272             centerH -= centerY;
36273             north.updateBox(this.safeBox(b));
36274         }
36275         if(south && south.isVisible()){
36276             var b = south.getBox();
36277             var m = south.getMargins();
36278             b.width = w - (m.left+m.right);
36279             b.x = m.left;
36280             var totalHeight = (b.height + m.top + m.bottom);
36281             b.y = h - totalHeight + m.top;
36282             centerH -= totalHeight;
36283             south.updateBox(this.safeBox(b));
36284         }
36285         if(west && west.isVisible()){
36286             var b = west.getBox();
36287             var m = west.getMargins();
36288             b.height = centerH - (m.top+m.bottom);
36289             b.x = m.left;
36290             b.y = centerY + m.top;
36291             var totalWidth = (b.width + m.left + m.right);
36292             centerX += totalWidth;
36293             centerW -= totalWidth;
36294             west.updateBox(this.safeBox(b));
36295         }
36296         if(east && east.isVisible()){
36297             var b = east.getBox();
36298             var m = east.getMargins();
36299             b.height = centerH - (m.top+m.bottom);
36300             var totalWidth = (b.width + m.left + m.right);
36301             b.x = w - totalWidth + m.left;
36302             b.y = centerY + m.top;
36303             centerW -= totalWidth;
36304             east.updateBox(this.safeBox(b));
36305         }
36306         if(center){
36307             var m = center.getMargins();
36308             var centerBox = {
36309                 x: centerX + m.left,
36310                 y: centerY + m.top,
36311                 width: centerW - (m.left+m.right),
36312                 height: centerH - (m.top+m.bottom)
36313             };
36314             //if(this.hideOnLayout){
36315                 //center.el.setStyle("display", "block");
36316             //}
36317             center.updateBox(this.safeBox(centerBox));
36318         }
36319         this.el.repaint();
36320         this.fireEvent("layout", this);
36321     },
36322
36323     // private
36324     safeBox : function(box){
36325         box.width = Math.max(0, box.width);
36326         box.height = Math.max(0, box.height);
36327         return box;
36328     },
36329
36330     /**
36331      * Adds a ContentPanel (or subclass) to this layout.
36332      * @param {String} target The target region key (north, south, east, west or center).
36333      * @param {Roo.ContentPanel} panel The panel to add
36334      * @return {Roo.ContentPanel} The added panel
36335      */
36336     add : function(target, panel){
36337          
36338         target = target.toLowerCase();
36339         return this.regions[target].add(panel);
36340     },
36341
36342     /**
36343      * Remove a ContentPanel (or subclass) to this layout.
36344      * @param {String} target The target region key (north, south, east, west or center).
36345      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36346      * @return {Roo.ContentPanel} The removed panel
36347      */
36348     remove : function(target, panel){
36349         target = target.toLowerCase();
36350         return this.regions[target].remove(panel);
36351     },
36352
36353     /**
36354      * Searches all regions for a panel with the specified id
36355      * @param {String} panelId
36356      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36357      */
36358     findPanel : function(panelId){
36359         var rs = this.regions;
36360         for(var target in rs){
36361             if(typeof rs[target] != "function"){
36362                 var p = rs[target].getPanel(panelId);
36363                 if(p){
36364                     return p;
36365                 }
36366             }
36367         }
36368         return null;
36369     },
36370
36371     /**
36372      * Searches all regions for a panel with the specified id and activates (shows) it.
36373      * @param {String/ContentPanel} panelId The panels id or the panel itself
36374      * @return {Roo.ContentPanel} The shown panel or null
36375      */
36376     showPanel : function(panelId) {
36377       var rs = this.regions;
36378       for(var target in rs){
36379          var r = rs[target];
36380          if(typeof r != "function"){
36381             if(r.hasPanel(panelId)){
36382                return r.showPanel(panelId);
36383             }
36384          }
36385       }
36386       return null;
36387    },
36388
36389    /**
36390      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36391      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36392      */
36393    /*
36394     restoreState : function(provider){
36395         if(!provider){
36396             provider = Roo.state.Manager;
36397         }
36398         var sm = new Roo.LayoutStateManager();
36399         sm.init(this, provider);
36400     },
36401 */
36402  
36403  
36404     /**
36405      * Adds a xtype elements to the layout.
36406      * <pre><code>
36407
36408 layout.addxtype({
36409        xtype : 'ContentPanel',
36410        region: 'west',
36411        items: [ .... ]
36412    }
36413 );
36414
36415 layout.addxtype({
36416         xtype : 'NestedLayoutPanel',
36417         region: 'west',
36418         layout: {
36419            center: { },
36420            west: { }   
36421         },
36422         items : [ ... list of content panels or nested layout panels.. ]
36423    }
36424 );
36425 </code></pre>
36426      * @param {Object} cfg Xtype definition of item to add.
36427      */
36428     addxtype : function(cfg)
36429     {
36430         // basically accepts a pannel...
36431         // can accept a layout region..!?!?
36432         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36433         
36434         
36435         // theory?  children can only be panels??
36436         
36437         //if (!cfg.xtype.match(/Panel$/)) {
36438         //    return false;
36439         //}
36440         var ret = false;
36441         
36442         if (typeof(cfg.region) == 'undefined') {
36443             Roo.log("Failed to add Panel, region was not set");
36444             Roo.log(cfg);
36445             return false;
36446         }
36447         var region = cfg.region;
36448         delete cfg.region;
36449         
36450           
36451         var xitems = [];
36452         if (cfg.items) {
36453             xitems = cfg.items;
36454             delete cfg.items;
36455         }
36456         var nb = false;
36457         
36458         if ( region == 'center') {
36459             Roo.log("Center: " + cfg.title);
36460         }
36461         
36462         
36463         switch(cfg.xtype) 
36464         {
36465             case 'Content':  // ContentPanel (el, cfg)
36466             case 'Scroll':  // ContentPanel (el, cfg)
36467             case 'View': 
36468                 cfg.autoCreate = cfg.autoCreate || true;
36469                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36470                 //} else {
36471                 //    var el = this.el.createChild();
36472                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36473                 //}
36474                 
36475                 this.add(region, ret);
36476                 break;
36477             
36478             /*
36479             case 'TreePanel': // our new panel!
36480                 cfg.el = this.el.createChild();
36481                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36482                 this.add(region, ret);
36483                 break;
36484             */
36485             
36486             case 'Nest': 
36487                 // create a new Layout (which is  a Border Layout...
36488                 
36489                 var clayout = cfg.layout;
36490                 clayout.el  = this.el.createChild();
36491                 clayout.items   = clayout.items  || [];
36492                 
36493                 delete cfg.layout;
36494                 
36495                 // replace this exitems with the clayout ones..
36496                 xitems = clayout.items;
36497                  
36498                 // force background off if it's in center...
36499                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36500                     cfg.background = false;
36501                 }
36502                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36503                 
36504                 
36505                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36506                 //console.log('adding nested layout panel '  + cfg.toSource());
36507                 this.add(region, ret);
36508                 nb = {}; /// find first...
36509                 break;
36510             
36511             case 'Grid':
36512                 
36513                 // needs grid and region
36514                 
36515                 //var el = this.getRegion(region).el.createChild();
36516                 /*
36517                  *var el = this.el.createChild();
36518                 // create the grid first...
36519                 cfg.grid.container = el;
36520                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36521                 */
36522                 
36523                 if (region == 'center' && this.active ) {
36524                     cfg.background = false;
36525                 }
36526                 
36527                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36528                 
36529                 this.add(region, ret);
36530                 /*
36531                 if (cfg.background) {
36532                     // render grid on panel activation (if panel background)
36533                     ret.on('activate', function(gp) {
36534                         if (!gp.grid.rendered) {
36535                     //        gp.grid.render(el);
36536                         }
36537                     });
36538                 } else {
36539                   //  cfg.grid.render(el);
36540                 }
36541                 */
36542                 break;
36543            
36544            
36545             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36546                 // it was the old xcomponent building that caused this before.
36547                 // espeically if border is the top element in the tree.
36548                 ret = this;
36549                 break; 
36550                 
36551                     
36552                 
36553                 
36554                 
36555             default:
36556                 /*
36557                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36558                     
36559                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36560                     this.add(region, ret);
36561                 } else {
36562                 */
36563                     Roo.log(cfg);
36564                     throw "Can not add '" + cfg.xtype + "' to Border";
36565                     return null;
36566              
36567                                 
36568              
36569         }
36570         this.beginUpdate();
36571         // add children..
36572         var region = '';
36573         var abn = {};
36574         Roo.each(xitems, function(i)  {
36575             region = nb && i.region ? i.region : false;
36576             
36577             var add = ret.addxtype(i);
36578            
36579             if (region) {
36580                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36581                 if (!i.background) {
36582                     abn[region] = nb[region] ;
36583                 }
36584             }
36585             
36586         });
36587         this.endUpdate();
36588
36589         // make the last non-background panel active..
36590         //if (nb) { Roo.log(abn); }
36591         if (nb) {
36592             
36593             for(var r in abn) {
36594                 region = this.getRegion(r);
36595                 if (region) {
36596                     // tried using nb[r], but it does not work..
36597                      
36598                     region.showPanel(abn[r]);
36599                    
36600                 }
36601             }
36602         }
36603         return ret;
36604         
36605     },
36606     
36607     
36608 // private
36609     factory : function(cfg)
36610     {
36611         
36612         var validRegions = Roo.bootstrap.layout.Border.regions;
36613
36614         var target = cfg.region;
36615         cfg.mgr = this;
36616         
36617         var r = Roo.bootstrap.layout;
36618         Roo.log(target);
36619         switch(target){
36620             case "north":
36621                 return new r.North(cfg);
36622             case "south":
36623                 return new r.South(cfg);
36624             case "east":
36625                 return new r.East(cfg);
36626             case "west":
36627                 return new r.West(cfg);
36628             case "center":
36629                 return new r.Center(cfg);
36630         }
36631         throw 'Layout region "'+target+'" not supported.';
36632     }
36633     
36634     
36635 });
36636  /*
36637  * Based on:
36638  * Ext JS Library 1.1.1
36639  * Copyright(c) 2006-2007, Ext JS, LLC.
36640  *
36641  * Originally Released Under LGPL - original licence link has changed is not relivant.
36642  *
36643  * Fork - LGPL
36644  * <script type="text/javascript">
36645  */
36646  
36647 /**
36648  * @class Roo.bootstrap.layout.Basic
36649  * @extends Roo.util.Observable
36650  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36651  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36652  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36653  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36654  * @cfg {string}   region  the region that it inhabits..
36655  * @cfg {bool}   skipConfig skip config?
36656  * 
36657
36658  */
36659 Roo.bootstrap.layout.Basic = function(config){
36660     
36661     this.mgr = config.mgr;
36662     
36663     this.position = config.region;
36664     
36665     var skipConfig = config.skipConfig;
36666     
36667     this.events = {
36668         /**
36669          * @scope Roo.BasicLayoutRegion
36670          */
36671         
36672         /**
36673          * @event beforeremove
36674          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36675          * @param {Roo.LayoutRegion} this
36676          * @param {Roo.ContentPanel} panel The panel
36677          * @param {Object} e The cancel event object
36678          */
36679         "beforeremove" : true,
36680         /**
36681          * @event invalidated
36682          * Fires when the layout for this region is changed.
36683          * @param {Roo.LayoutRegion} this
36684          */
36685         "invalidated" : true,
36686         /**
36687          * @event visibilitychange
36688          * Fires when this region is shown or hidden 
36689          * @param {Roo.LayoutRegion} this
36690          * @param {Boolean} visibility true or false
36691          */
36692         "visibilitychange" : true,
36693         /**
36694          * @event paneladded
36695          * Fires when a panel is added. 
36696          * @param {Roo.LayoutRegion} this
36697          * @param {Roo.ContentPanel} panel The panel
36698          */
36699         "paneladded" : true,
36700         /**
36701          * @event panelremoved
36702          * Fires when a panel is removed. 
36703          * @param {Roo.LayoutRegion} this
36704          * @param {Roo.ContentPanel} panel The panel
36705          */
36706         "panelremoved" : true,
36707         /**
36708          * @event beforecollapse
36709          * Fires when this region before collapse.
36710          * @param {Roo.LayoutRegion} this
36711          */
36712         "beforecollapse" : true,
36713         /**
36714          * @event collapsed
36715          * Fires when this region is collapsed.
36716          * @param {Roo.LayoutRegion} this
36717          */
36718         "collapsed" : true,
36719         /**
36720          * @event expanded
36721          * Fires when this region is expanded.
36722          * @param {Roo.LayoutRegion} this
36723          */
36724         "expanded" : true,
36725         /**
36726          * @event slideshow
36727          * Fires when this region is slid into view.
36728          * @param {Roo.LayoutRegion} this
36729          */
36730         "slideshow" : true,
36731         /**
36732          * @event slidehide
36733          * Fires when this region slides out of view. 
36734          * @param {Roo.LayoutRegion} this
36735          */
36736         "slidehide" : true,
36737         /**
36738          * @event panelactivated
36739          * Fires when a panel is activated. 
36740          * @param {Roo.LayoutRegion} this
36741          * @param {Roo.ContentPanel} panel The activated panel
36742          */
36743         "panelactivated" : true,
36744         /**
36745          * @event resized
36746          * Fires when the user resizes this region. 
36747          * @param {Roo.LayoutRegion} this
36748          * @param {Number} newSize The new size (width for east/west, height for north/south)
36749          */
36750         "resized" : true
36751     };
36752     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36753     this.panels = new Roo.util.MixedCollection();
36754     this.panels.getKey = this.getPanelId.createDelegate(this);
36755     this.box = null;
36756     this.activePanel = null;
36757     // ensure listeners are added...
36758     
36759     if (config.listeners || config.events) {
36760         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36761             listeners : config.listeners || {},
36762             events : config.events || {}
36763         });
36764     }
36765     
36766     if(skipConfig !== true){
36767         this.applyConfig(config);
36768     }
36769 };
36770
36771 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36772 {
36773     getPanelId : function(p){
36774         return p.getId();
36775     },
36776     
36777     applyConfig : function(config){
36778         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36779         this.config = config;
36780         
36781     },
36782     
36783     /**
36784      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36785      * the width, for horizontal (north, south) the height.
36786      * @param {Number} newSize The new width or height
36787      */
36788     resizeTo : function(newSize){
36789         var el = this.el ? this.el :
36790                  (this.activePanel ? this.activePanel.getEl() : null);
36791         if(el){
36792             switch(this.position){
36793                 case "east":
36794                 case "west":
36795                     el.setWidth(newSize);
36796                     this.fireEvent("resized", this, newSize);
36797                 break;
36798                 case "north":
36799                 case "south":
36800                     el.setHeight(newSize);
36801                     this.fireEvent("resized", this, newSize);
36802                 break;                
36803             }
36804         }
36805     },
36806     
36807     getBox : function(){
36808         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36809     },
36810     
36811     getMargins : function(){
36812         return this.margins;
36813     },
36814     
36815     updateBox : function(box){
36816         this.box = box;
36817         var el = this.activePanel.getEl();
36818         el.dom.style.left = box.x + "px";
36819         el.dom.style.top = box.y + "px";
36820         this.activePanel.setSize(box.width, box.height);
36821     },
36822     
36823     /**
36824      * Returns the container element for this region.
36825      * @return {Roo.Element}
36826      */
36827     getEl : function(){
36828         return this.activePanel;
36829     },
36830     
36831     /**
36832      * Returns true if this region is currently visible.
36833      * @return {Boolean}
36834      */
36835     isVisible : function(){
36836         return this.activePanel ? true : false;
36837     },
36838     
36839     setActivePanel : function(panel){
36840         panel = this.getPanel(panel);
36841         if(this.activePanel && this.activePanel != panel){
36842             this.activePanel.setActiveState(false);
36843             this.activePanel.getEl().setLeftTop(-10000,-10000);
36844         }
36845         this.activePanel = panel;
36846         panel.setActiveState(true);
36847         if(this.box){
36848             panel.setSize(this.box.width, this.box.height);
36849         }
36850         this.fireEvent("panelactivated", this, panel);
36851         this.fireEvent("invalidated");
36852     },
36853     
36854     /**
36855      * Show the specified panel.
36856      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36857      * @return {Roo.ContentPanel} The shown panel or null
36858      */
36859     showPanel : function(panel){
36860         panel = this.getPanel(panel);
36861         if(panel){
36862             this.setActivePanel(panel);
36863         }
36864         return panel;
36865     },
36866     
36867     /**
36868      * Get the active panel for this region.
36869      * @return {Roo.ContentPanel} The active panel or null
36870      */
36871     getActivePanel : function(){
36872         return this.activePanel;
36873     },
36874     
36875     /**
36876      * Add the passed ContentPanel(s)
36877      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36878      * @return {Roo.ContentPanel} The panel added (if only one was added)
36879      */
36880     add : function(panel){
36881         if(arguments.length > 1){
36882             for(var i = 0, len = arguments.length; i < len; i++) {
36883                 this.add(arguments[i]);
36884             }
36885             return null;
36886         }
36887         if(this.hasPanel(panel)){
36888             this.showPanel(panel);
36889             return panel;
36890         }
36891         var el = panel.getEl();
36892         if(el.dom.parentNode != this.mgr.el.dom){
36893             this.mgr.el.dom.appendChild(el.dom);
36894         }
36895         if(panel.setRegion){
36896             panel.setRegion(this);
36897         }
36898         this.panels.add(panel);
36899         el.setStyle("position", "absolute");
36900         if(!panel.background){
36901             this.setActivePanel(panel);
36902             if(this.config.initialSize && this.panels.getCount()==1){
36903                 this.resizeTo(this.config.initialSize);
36904             }
36905         }
36906         this.fireEvent("paneladded", this, panel);
36907         return panel;
36908     },
36909     
36910     /**
36911      * Returns true if the panel is in this region.
36912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36913      * @return {Boolean}
36914      */
36915     hasPanel : function(panel){
36916         if(typeof panel == "object"){ // must be panel obj
36917             panel = panel.getId();
36918         }
36919         return this.getPanel(panel) ? true : false;
36920     },
36921     
36922     /**
36923      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36924      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36925      * @param {Boolean} preservePanel Overrides the config preservePanel option
36926      * @return {Roo.ContentPanel} The panel that was removed
36927      */
36928     remove : function(panel, preservePanel){
36929         panel = this.getPanel(panel);
36930         if(!panel){
36931             return null;
36932         }
36933         var e = {};
36934         this.fireEvent("beforeremove", this, panel, e);
36935         if(e.cancel === true){
36936             return null;
36937         }
36938         var panelId = panel.getId();
36939         this.panels.removeKey(panelId);
36940         return panel;
36941     },
36942     
36943     /**
36944      * Returns the panel specified or null if it's not in this region.
36945      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36946      * @return {Roo.ContentPanel}
36947      */
36948     getPanel : function(id){
36949         if(typeof id == "object"){ // must be panel obj
36950             return id;
36951         }
36952         return this.panels.get(id);
36953     },
36954     
36955     /**
36956      * Returns this regions position (north/south/east/west/center).
36957      * @return {String} 
36958      */
36959     getPosition: function(){
36960         return this.position;    
36961     }
36962 });/*
36963  * Based on:
36964  * Ext JS Library 1.1.1
36965  * Copyright(c) 2006-2007, Ext JS, LLC.
36966  *
36967  * Originally Released Under LGPL - original licence link has changed is not relivant.
36968  *
36969  * Fork - LGPL
36970  * <script type="text/javascript">
36971  */
36972  
36973 /**
36974  * @class Roo.bootstrap.layout.Region
36975  * @extends Roo.bootstrap.layout.Basic
36976  * This class represents a region in a layout manager.
36977  
36978  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36979  * @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})
36980  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36981  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36982  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36983  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36984  * @cfg {String}    title           The title for the region (overrides panel titles)
36985  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36986  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36987  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36988  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36989  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36990  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36991  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36992  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36993  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36994  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36995
36996  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36997  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36998  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36999  * @cfg {Number}    width           For East/West panels
37000  * @cfg {Number}    height          For North/South panels
37001  * @cfg {Boolean}   split           To show the splitter
37002  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37003  * 
37004  * @cfg {string}   cls             Extra CSS classes to add to region
37005  * 
37006  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37007  * @cfg {string}   region  the region that it inhabits..
37008  *
37009
37010  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37011  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37012
37013  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37014  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37015  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37016  */
37017 Roo.bootstrap.layout.Region = function(config)
37018 {
37019     this.applyConfig(config);
37020
37021     var mgr = config.mgr;
37022     var pos = config.region;
37023     config.skipConfig = true;
37024     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37025     
37026     if (mgr.el) {
37027         this.onRender(mgr.el);   
37028     }
37029      
37030     this.visible = true;
37031     this.collapsed = false;
37032     this.unrendered_panels = [];
37033 };
37034
37035 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37036
37037     position: '', // set by wrapper (eg. north/south etc..)
37038     unrendered_panels : null,  // unrendered panels.
37039     
37040     tabPosition : false,
37041     
37042     mgr: false, // points to 'Border'
37043     
37044     
37045     createBody : function(){
37046         /** This region's body element 
37047         * @type Roo.Element */
37048         this.bodyEl = this.el.createChild({
37049                 tag: "div",
37050                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37051         });
37052     },
37053
37054     onRender: function(ctr, pos)
37055     {
37056         var dh = Roo.DomHelper;
37057         /** This region's container element 
37058         * @type Roo.Element */
37059         this.el = dh.append(ctr.dom, {
37060                 tag: "div",
37061                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37062             }, true);
37063         /** This region's title element 
37064         * @type Roo.Element */
37065     
37066         this.titleEl = dh.append(this.el.dom,  {
37067                 tag: "div",
37068                 unselectable: "on",
37069                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37070                 children:[
37071                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37072                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37073                 ]
37074             }, true);
37075         
37076         this.titleEl.enableDisplayMode();
37077         /** This region's title text element 
37078         * @type HTMLElement */
37079         this.titleTextEl = this.titleEl.dom.firstChild;
37080         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37081         /*
37082         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37083         this.closeBtn.enableDisplayMode();
37084         this.closeBtn.on("click", this.closeClicked, this);
37085         this.closeBtn.hide();
37086     */
37087         this.createBody(this.config);
37088         if(this.config.hideWhenEmpty){
37089             this.hide();
37090             this.on("paneladded", this.validateVisibility, this);
37091             this.on("panelremoved", this.validateVisibility, this);
37092         }
37093         if(this.autoScroll){
37094             this.bodyEl.setStyle("overflow", "auto");
37095         }else{
37096             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37097         }
37098         //if(c.titlebar !== false){
37099             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37100                 this.titleEl.hide();
37101             }else{
37102                 this.titleEl.show();
37103                 if(this.config.title){
37104                     this.titleTextEl.innerHTML = this.config.title;
37105                 }
37106             }
37107         //}
37108         if(this.config.collapsed){
37109             this.collapse(true);
37110         }
37111         if(this.config.hidden){
37112             this.hide();
37113         }
37114         
37115         if (this.unrendered_panels && this.unrendered_panels.length) {
37116             for (var i =0;i< this.unrendered_panels.length; i++) {
37117                 this.add(this.unrendered_panels[i]);
37118             }
37119             this.unrendered_panels = null;
37120             
37121         }
37122         
37123     },
37124     
37125     applyConfig : function(c)
37126     {
37127         /*
37128          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37129             var dh = Roo.DomHelper;
37130             if(c.titlebar !== false){
37131                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37132                 this.collapseBtn.on("click", this.collapse, this);
37133                 this.collapseBtn.enableDisplayMode();
37134                 /*
37135                 if(c.showPin === true || this.showPin){
37136                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37137                     this.stickBtn.enableDisplayMode();
37138                     this.stickBtn.on("click", this.expand, this);
37139                     this.stickBtn.hide();
37140                 }
37141                 
37142             }
37143             */
37144             /** This region's collapsed element
37145             * @type Roo.Element */
37146             /*
37147              *
37148             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37149                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37150             ]}, true);
37151             
37152             if(c.floatable !== false){
37153                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37154                this.collapsedEl.on("click", this.collapseClick, this);
37155             }
37156
37157             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37158                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37159                    id: "message", unselectable: "on", style:{"float":"left"}});
37160                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37161              }
37162             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37163             this.expandBtn.on("click", this.expand, this);
37164             
37165         }
37166         
37167         if(this.collapseBtn){
37168             this.collapseBtn.setVisible(c.collapsible == true);
37169         }
37170         
37171         this.cmargins = c.cmargins || this.cmargins ||
37172                          (this.position == "west" || this.position == "east" ?
37173                              {top: 0, left: 2, right:2, bottom: 0} :
37174                              {top: 2, left: 0, right:0, bottom: 2});
37175         */
37176         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37177         
37178         
37179         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37180         
37181         this.autoScroll = c.autoScroll || false;
37182         
37183         
37184        
37185         
37186         this.duration = c.duration || .30;
37187         this.slideDuration = c.slideDuration || .45;
37188         this.config = c;
37189        
37190     },
37191     /**
37192      * Returns true if this region is currently visible.
37193      * @return {Boolean}
37194      */
37195     isVisible : function(){
37196         return this.visible;
37197     },
37198
37199     /**
37200      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37201      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37202      */
37203     //setCollapsedTitle : function(title){
37204     //    title = title || "&#160;";
37205      //   if(this.collapsedTitleTextEl){
37206       //      this.collapsedTitleTextEl.innerHTML = title;
37207        // }
37208     //},
37209
37210     getBox : function(){
37211         var b;
37212       //  if(!this.collapsed){
37213             b = this.el.getBox(false, true);
37214        // }else{
37215           //  b = this.collapsedEl.getBox(false, true);
37216         //}
37217         return b;
37218     },
37219
37220     getMargins : function(){
37221         return this.margins;
37222         //return this.collapsed ? this.cmargins : this.margins;
37223     },
37224 /*
37225     highlight : function(){
37226         this.el.addClass("x-layout-panel-dragover");
37227     },
37228
37229     unhighlight : function(){
37230         this.el.removeClass("x-layout-panel-dragover");
37231     },
37232 */
37233     updateBox : function(box)
37234     {
37235         if (!this.bodyEl) {
37236             return; // not rendered yet..
37237         }
37238         
37239         this.box = box;
37240         if(!this.collapsed){
37241             this.el.dom.style.left = box.x + "px";
37242             this.el.dom.style.top = box.y + "px";
37243             this.updateBody(box.width, box.height);
37244         }else{
37245             this.collapsedEl.dom.style.left = box.x + "px";
37246             this.collapsedEl.dom.style.top = box.y + "px";
37247             this.collapsedEl.setSize(box.width, box.height);
37248         }
37249         if(this.tabs){
37250             this.tabs.autoSizeTabs();
37251         }
37252     },
37253
37254     updateBody : function(w, h)
37255     {
37256         if(w !== null){
37257             this.el.setWidth(w);
37258             w -= this.el.getBorderWidth("rl");
37259             if(this.config.adjustments){
37260                 w += this.config.adjustments[0];
37261             }
37262         }
37263         if(h !== null && h > 0){
37264             this.el.setHeight(h);
37265             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37266             h -= this.el.getBorderWidth("tb");
37267             if(this.config.adjustments){
37268                 h += this.config.adjustments[1];
37269             }
37270             this.bodyEl.setHeight(h);
37271             if(this.tabs){
37272                 h = this.tabs.syncHeight(h);
37273             }
37274         }
37275         if(this.panelSize){
37276             w = w !== null ? w : this.panelSize.width;
37277             h = h !== null ? h : this.panelSize.height;
37278         }
37279         if(this.activePanel){
37280             var el = this.activePanel.getEl();
37281             w = w !== null ? w : el.getWidth();
37282             h = h !== null ? h : el.getHeight();
37283             this.panelSize = {width: w, height: h};
37284             this.activePanel.setSize(w, h);
37285         }
37286         if(Roo.isIE && this.tabs){
37287             this.tabs.el.repaint();
37288         }
37289     },
37290
37291     /**
37292      * Returns the container element for this region.
37293      * @return {Roo.Element}
37294      */
37295     getEl : function(){
37296         return this.el;
37297     },
37298
37299     /**
37300      * Hides this region.
37301      */
37302     hide : function(){
37303         //if(!this.collapsed){
37304             this.el.dom.style.left = "-2000px";
37305             this.el.hide();
37306         //}else{
37307          //   this.collapsedEl.dom.style.left = "-2000px";
37308          //   this.collapsedEl.hide();
37309        // }
37310         this.visible = false;
37311         this.fireEvent("visibilitychange", this, false);
37312     },
37313
37314     /**
37315      * Shows this region if it was previously hidden.
37316      */
37317     show : function(){
37318         //if(!this.collapsed){
37319             this.el.show();
37320         //}else{
37321         //    this.collapsedEl.show();
37322        // }
37323         this.visible = true;
37324         this.fireEvent("visibilitychange", this, true);
37325     },
37326 /*
37327     closeClicked : function(){
37328         if(this.activePanel){
37329             this.remove(this.activePanel);
37330         }
37331     },
37332
37333     collapseClick : function(e){
37334         if(this.isSlid){
37335            e.stopPropagation();
37336            this.slideIn();
37337         }else{
37338            e.stopPropagation();
37339            this.slideOut();
37340         }
37341     },
37342 */
37343     /**
37344      * Collapses this region.
37345      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37346      */
37347     /*
37348     collapse : function(skipAnim, skipCheck = false){
37349         if(this.collapsed) {
37350             return;
37351         }
37352         
37353         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37354             
37355             this.collapsed = true;
37356             if(this.split){
37357                 this.split.el.hide();
37358             }
37359             if(this.config.animate && skipAnim !== true){
37360                 this.fireEvent("invalidated", this);
37361                 this.animateCollapse();
37362             }else{
37363                 this.el.setLocation(-20000,-20000);
37364                 this.el.hide();
37365                 this.collapsedEl.show();
37366                 this.fireEvent("collapsed", this);
37367                 this.fireEvent("invalidated", this);
37368             }
37369         }
37370         
37371     },
37372 */
37373     animateCollapse : function(){
37374         // overridden
37375     },
37376
37377     /**
37378      * Expands this region if it was previously collapsed.
37379      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37380      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37381      */
37382     /*
37383     expand : function(e, skipAnim){
37384         if(e) {
37385             e.stopPropagation();
37386         }
37387         if(!this.collapsed || this.el.hasActiveFx()) {
37388             return;
37389         }
37390         if(this.isSlid){
37391             this.afterSlideIn();
37392             skipAnim = true;
37393         }
37394         this.collapsed = false;
37395         if(this.config.animate && skipAnim !== true){
37396             this.animateExpand();
37397         }else{
37398             this.el.show();
37399             if(this.split){
37400                 this.split.el.show();
37401             }
37402             this.collapsedEl.setLocation(-2000,-2000);
37403             this.collapsedEl.hide();
37404             this.fireEvent("invalidated", this);
37405             this.fireEvent("expanded", this);
37406         }
37407     },
37408 */
37409     animateExpand : function(){
37410         // overridden
37411     },
37412
37413     initTabs : function()
37414     {
37415         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37416         
37417         var ts = new Roo.bootstrap.panel.Tabs({
37418             el: this.bodyEl.dom,
37419             region : this,
37420             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37421             disableTooltips: this.config.disableTabTips,
37422             toolbar : this.config.toolbar
37423         });
37424         
37425         if(this.config.hideTabs){
37426             ts.stripWrap.setDisplayed(false);
37427         }
37428         this.tabs = ts;
37429         ts.resizeTabs = this.config.resizeTabs === true;
37430         ts.minTabWidth = this.config.minTabWidth || 40;
37431         ts.maxTabWidth = this.config.maxTabWidth || 250;
37432         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37433         ts.monitorResize = false;
37434         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37435         ts.bodyEl.addClass('roo-layout-tabs-body');
37436         this.panels.each(this.initPanelAsTab, this);
37437     },
37438
37439     initPanelAsTab : function(panel){
37440         var ti = this.tabs.addTab(
37441             panel.getEl().id,
37442             panel.getTitle(),
37443             null,
37444             this.config.closeOnTab && panel.isClosable(),
37445             panel.tpl
37446         );
37447         if(panel.tabTip !== undefined){
37448             ti.setTooltip(panel.tabTip);
37449         }
37450         ti.on("activate", function(){
37451               this.setActivePanel(panel);
37452         }, this);
37453         
37454         if(this.config.closeOnTab){
37455             ti.on("beforeclose", function(t, e){
37456                 e.cancel = true;
37457                 this.remove(panel);
37458             }, this);
37459         }
37460         
37461         panel.tabItem = ti;
37462         
37463         return ti;
37464     },
37465
37466     updatePanelTitle : function(panel, title)
37467     {
37468         if(this.activePanel == panel){
37469             this.updateTitle(title);
37470         }
37471         if(this.tabs){
37472             var ti = this.tabs.getTab(panel.getEl().id);
37473             ti.setText(title);
37474             if(panel.tabTip !== undefined){
37475                 ti.setTooltip(panel.tabTip);
37476             }
37477         }
37478     },
37479
37480     updateTitle : function(title){
37481         if(this.titleTextEl && !this.config.title){
37482             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37483         }
37484     },
37485
37486     setActivePanel : function(panel)
37487     {
37488         panel = this.getPanel(panel);
37489         if(this.activePanel && this.activePanel != panel){
37490             if(this.activePanel.setActiveState(false) === false){
37491                 return;
37492             }
37493         }
37494         this.activePanel = panel;
37495         panel.setActiveState(true);
37496         if(this.panelSize){
37497             panel.setSize(this.panelSize.width, this.panelSize.height);
37498         }
37499         if(this.closeBtn){
37500             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37501         }
37502         this.updateTitle(panel.getTitle());
37503         if(this.tabs){
37504             this.fireEvent("invalidated", this);
37505         }
37506         this.fireEvent("panelactivated", this, panel);
37507     },
37508
37509     /**
37510      * Shows the specified panel.
37511      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37512      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37513      */
37514     showPanel : function(panel)
37515     {
37516         panel = this.getPanel(panel);
37517         if(panel){
37518             if(this.tabs){
37519                 var tab = this.tabs.getTab(panel.getEl().id);
37520                 if(tab.isHidden()){
37521                     this.tabs.unhideTab(tab.id);
37522                 }
37523                 tab.activate();
37524             }else{
37525                 this.setActivePanel(panel);
37526             }
37527         }
37528         return panel;
37529     },
37530
37531     /**
37532      * Get the active panel for this region.
37533      * @return {Roo.ContentPanel} The active panel or null
37534      */
37535     getActivePanel : function(){
37536         return this.activePanel;
37537     },
37538
37539     validateVisibility : function(){
37540         if(this.panels.getCount() < 1){
37541             this.updateTitle("&#160;");
37542             this.closeBtn.hide();
37543             this.hide();
37544         }else{
37545             if(!this.isVisible()){
37546                 this.show();
37547             }
37548         }
37549     },
37550
37551     /**
37552      * Adds the passed ContentPanel(s) to this region.
37553      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37554      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37555      */
37556     add : function(panel)
37557     {
37558         if(arguments.length > 1){
37559             for(var i = 0, len = arguments.length; i < len; i++) {
37560                 this.add(arguments[i]);
37561             }
37562             return null;
37563         }
37564         
37565         // if we have not been rendered yet, then we can not really do much of this..
37566         if (!this.bodyEl) {
37567             this.unrendered_panels.push(panel);
37568             return panel;
37569         }
37570         
37571         
37572         
37573         
37574         if(this.hasPanel(panel)){
37575             this.showPanel(panel);
37576             return panel;
37577         }
37578         panel.setRegion(this);
37579         this.panels.add(panel);
37580        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37581             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37582             // and hide them... ???
37583             this.bodyEl.dom.appendChild(panel.getEl().dom);
37584             if(panel.background !== true){
37585                 this.setActivePanel(panel);
37586             }
37587             this.fireEvent("paneladded", this, panel);
37588             return panel;
37589         }
37590         */
37591         if(!this.tabs){
37592             this.initTabs();
37593         }else{
37594             this.initPanelAsTab(panel);
37595         }
37596         
37597         
37598         if(panel.background !== true){
37599             this.tabs.activate(panel.getEl().id);
37600         }
37601         this.fireEvent("paneladded", this, panel);
37602         return panel;
37603     },
37604
37605     /**
37606      * Hides the tab for the specified panel.
37607      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37608      */
37609     hidePanel : function(panel){
37610         if(this.tabs && (panel = this.getPanel(panel))){
37611             this.tabs.hideTab(panel.getEl().id);
37612         }
37613     },
37614
37615     /**
37616      * Unhides the tab for a previously hidden panel.
37617      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37618      */
37619     unhidePanel : function(panel){
37620         if(this.tabs && (panel = this.getPanel(panel))){
37621             this.tabs.unhideTab(panel.getEl().id);
37622         }
37623     },
37624
37625     clearPanels : function(){
37626         while(this.panels.getCount() > 0){
37627              this.remove(this.panels.first());
37628         }
37629     },
37630
37631     /**
37632      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37633      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37634      * @param {Boolean} preservePanel Overrides the config preservePanel option
37635      * @return {Roo.ContentPanel} The panel that was removed
37636      */
37637     remove : function(panel, preservePanel)
37638     {
37639         panel = this.getPanel(panel);
37640         if(!panel){
37641             return null;
37642         }
37643         var e = {};
37644         this.fireEvent("beforeremove", this, panel, e);
37645         if(e.cancel === true){
37646             return null;
37647         }
37648         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37649         var panelId = panel.getId();
37650         this.panels.removeKey(panelId);
37651         if(preservePanel){
37652             document.body.appendChild(panel.getEl().dom);
37653         }
37654         if(this.tabs){
37655             this.tabs.removeTab(panel.getEl().id);
37656         }else if (!preservePanel){
37657             this.bodyEl.dom.removeChild(panel.getEl().dom);
37658         }
37659         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37660             var p = this.panels.first();
37661             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37662             tempEl.appendChild(p.getEl().dom);
37663             this.bodyEl.update("");
37664             this.bodyEl.dom.appendChild(p.getEl().dom);
37665             tempEl = null;
37666             this.updateTitle(p.getTitle());
37667             this.tabs = null;
37668             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37669             this.setActivePanel(p);
37670         }
37671         panel.setRegion(null);
37672         if(this.activePanel == panel){
37673             this.activePanel = null;
37674         }
37675         if(this.config.autoDestroy !== false && preservePanel !== true){
37676             try{panel.destroy();}catch(e){}
37677         }
37678         this.fireEvent("panelremoved", this, panel);
37679         return panel;
37680     },
37681
37682     /**
37683      * Returns the TabPanel component used by this region
37684      * @return {Roo.TabPanel}
37685      */
37686     getTabs : function(){
37687         return this.tabs;
37688     },
37689
37690     createTool : function(parentEl, className){
37691         var btn = Roo.DomHelper.append(parentEl, {
37692             tag: "div",
37693             cls: "x-layout-tools-button",
37694             children: [ {
37695                 tag: "div",
37696                 cls: "roo-layout-tools-button-inner " + className,
37697                 html: "&#160;"
37698             }]
37699         }, true);
37700         btn.addClassOnOver("roo-layout-tools-button-over");
37701         return btn;
37702     }
37703 });/*
37704  * Based on:
37705  * Ext JS Library 1.1.1
37706  * Copyright(c) 2006-2007, Ext JS, LLC.
37707  *
37708  * Originally Released Under LGPL - original licence link has changed is not relivant.
37709  *
37710  * Fork - LGPL
37711  * <script type="text/javascript">
37712  */
37713  
37714
37715
37716 /**
37717  * @class Roo.SplitLayoutRegion
37718  * @extends Roo.LayoutRegion
37719  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37720  */
37721 Roo.bootstrap.layout.Split = function(config){
37722     this.cursor = config.cursor;
37723     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37724 };
37725
37726 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37727 {
37728     splitTip : "Drag to resize.",
37729     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37730     useSplitTips : false,
37731
37732     applyConfig : function(config){
37733         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37734     },
37735     
37736     onRender : function(ctr,pos) {
37737         
37738         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37739         if(!this.config.split){
37740             return;
37741         }
37742         if(!this.split){
37743             
37744             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37745                             tag: "div",
37746                             id: this.el.id + "-split",
37747                             cls: "roo-layout-split roo-layout-split-"+this.position,
37748                             html: "&#160;"
37749             });
37750             /** The SplitBar for this region 
37751             * @type Roo.SplitBar */
37752             // does not exist yet...
37753             Roo.log([this.position, this.orientation]);
37754             
37755             this.split = new Roo.bootstrap.SplitBar({
37756                 dragElement : splitEl,
37757                 resizingElement: this.el,
37758                 orientation : this.orientation
37759             });
37760             
37761             this.split.on("moved", this.onSplitMove, this);
37762             this.split.useShim = this.config.useShim === true;
37763             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37764             if(this.useSplitTips){
37765                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37766             }
37767             //if(config.collapsible){
37768             //    this.split.el.on("dblclick", this.collapse,  this);
37769             //}
37770         }
37771         if(typeof this.config.minSize != "undefined"){
37772             this.split.minSize = this.config.minSize;
37773         }
37774         if(typeof this.config.maxSize != "undefined"){
37775             this.split.maxSize = this.config.maxSize;
37776         }
37777         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37778             this.hideSplitter();
37779         }
37780         
37781     },
37782
37783     getHMaxSize : function(){
37784          var cmax = this.config.maxSize || 10000;
37785          var center = this.mgr.getRegion("center");
37786          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37787     },
37788
37789     getVMaxSize : function(){
37790          var cmax = this.config.maxSize || 10000;
37791          var center = this.mgr.getRegion("center");
37792          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37793     },
37794
37795     onSplitMove : function(split, newSize){
37796         this.fireEvent("resized", this, newSize);
37797     },
37798     
37799     /** 
37800      * Returns the {@link Roo.SplitBar} for this region.
37801      * @return {Roo.SplitBar}
37802      */
37803     getSplitBar : function(){
37804         return this.split;
37805     },
37806     
37807     hide : function(){
37808         this.hideSplitter();
37809         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37810     },
37811
37812     hideSplitter : function(){
37813         if(this.split){
37814             this.split.el.setLocation(-2000,-2000);
37815             this.split.el.hide();
37816         }
37817     },
37818
37819     show : function(){
37820         if(this.split){
37821             this.split.el.show();
37822         }
37823         Roo.bootstrap.layout.Split.superclass.show.call(this);
37824     },
37825     
37826     beforeSlide: function(){
37827         if(Roo.isGecko){// firefox overflow auto bug workaround
37828             this.bodyEl.clip();
37829             if(this.tabs) {
37830                 this.tabs.bodyEl.clip();
37831             }
37832             if(this.activePanel){
37833                 this.activePanel.getEl().clip();
37834                 
37835                 if(this.activePanel.beforeSlide){
37836                     this.activePanel.beforeSlide();
37837                 }
37838             }
37839         }
37840     },
37841     
37842     afterSlide : function(){
37843         if(Roo.isGecko){// firefox overflow auto bug workaround
37844             this.bodyEl.unclip();
37845             if(this.tabs) {
37846                 this.tabs.bodyEl.unclip();
37847             }
37848             if(this.activePanel){
37849                 this.activePanel.getEl().unclip();
37850                 if(this.activePanel.afterSlide){
37851                     this.activePanel.afterSlide();
37852                 }
37853             }
37854         }
37855     },
37856
37857     initAutoHide : function(){
37858         if(this.autoHide !== false){
37859             if(!this.autoHideHd){
37860                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37861                 this.autoHideHd = {
37862                     "mouseout": function(e){
37863                         if(!e.within(this.el, true)){
37864                             st.delay(500);
37865                         }
37866                     },
37867                     "mouseover" : function(e){
37868                         st.cancel();
37869                     },
37870                     scope : this
37871                 };
37872             }
37873             this.el.on(this.autoHideHd);
37874         }
37875     },
37876
37877     clearAutoHide : function(){
37878         if(this.autoHide !== false){
37879             this.el.un("mouseout", this.autoHideHd.mouseout);
37880             this.el.un("mouseover", this.autoHideHd.mouseover);
37881         }
37882     },
37883
37884     clearMonitor : function(){
37885         Roo.get(document).un("click", this.slideInIf, this);
37886     },
37887
37888     // these names are backwards but not changed for compat
37889     slideOut : function(){
37890         if(this.isSlid || this.el.hasActiveFx()){
37891             return;
37892         }
37893         this.isSlid = true;
37894         if(this.collapseBtn){
37895             this.collapseBtn.hide();
37896         }
37897         this.closeBtnState = this.closeBtn.getStyle('display');
37898         this.closeBtn.hide();
37899         if(this.stickBtn){
37900             this.stickBtn.show();
37901         }
37902         this.el.show();
37903         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37904         this.beforeSlide();
37905         this.el.setStyle("z-index", 10001);
37906         this.el.slideIn(this.getSlideAnchor(), {
37907             callback: function(){
37908                 this.afterSlide();
37909                 this.initAutoHide();
37910                 Roo.get(document).on("click", this.slideInIf, this);
37911                 this.fireEvent("slideshow", this);
37912             },
37913             scope: this,
37914             block: true
37915         });
37916     },
37917
37918     afterSlideIn : function(){
37919         this.clearAutoHide();
37920         this.isSlid = false;
37921         this.clearMonitor();
37922         this.el.setStyle("z-index", "");
37923         if(this.collapseBtn){
37924             this.collapseBtn.show();
37925         }
37926         this.closeBtn.setStyle('display', this.closeBtnState);
37927         if(this.stickBtn){
37928             this.stickBtn.hide();
37929         }
37930         this.fireEvent("slidehide", this);
37931     },
37932
37933     slideIn : function(cb){
37934         if(!this.isSlid || this.el.hasActiveFx()){
37935             Roo.callback(cb);
37936             return;
37937         }
37938         this.isSlid = false;
37939         this.beforeSlide();
37940         this.el.slideOut(this.getSlideAnchor(), {
37941             callback: function(){
37942                 this.el.setLeftTop(-10000, -10000);
37943                 this.afterSlide();
37944                 this.afterSlideIn();
37945                 Roo.callback(cb);
37946             },
37947             scope: this,
37948             block: true
37949         });
37950     },
37951     
37952     slideInIf : function(e){
37953         if(!e.within(this.el)){
37954             this.slideIn();
37955         }
37956     },
37957
37958     animateCollapse : function(){
37959         this.beforeSlide();
37960         this.el.setStyle("z-index", 20000);
37961         var anchor = this.getSlideAnchor();
37962         this.el.slideOut(anchor, {
37963             callback : function(){
37964                 this.el.setStyle("z-index", "");
37965                 this.collapsedEl.slideIn(anchor, {duration:.3});
37966                 this.afterSlide();
37967                 this.el.setLocation(-10000,-10000);
37968                 this.el.hide();
37969                 this.fireEvent("collapsed", this);
37970             },
37971             scope: this,
37972             block: true
37973         });
37974     },
37975
37976     animateExpand : function(){
37977         this.beforeSlide();
37978         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37979         this.el.setStyle("z-index", 20000);
37980         this.collapsedEl.hide({
37981             duration:.1
37982         });
37983         this.el.slideIn(this.getSlideAnchor(), {
37984             callback : function(){
37985                 this.el.setStyle("z-index", "");
37986                 this.afterSlide();
37987                 if(this.split){
37988                     this.split.el.show();
37989                 }
37990                 this.fireEvent("invalidated", this);
37991                 this.fireEvent("expanded", this);
37992             },
37993             scope: this,
37994             block: true
37995         });
37996     },
37997
37998     anchors : {
37999         "west" : "left",
38000         "east" : "right",
38001         "north" : "top",
38002         "south" : "bottom"
38003     },
38004
38005     sanchors : {
38006         "west" : "l",
38007         "east" : "r",
38008         "north" : "t",
38009         "south" : "b"
38010     },
38011
38012     canchors : {
38013         "west" : "tl-tr",
38014         "east" : "tr-tl",
38015         "north" : "tl-bl",
38016         "south" : "bl-tl"
38017     },
38018
38019     getAnchor : function(){
38020         return this.anchors[this.position];
38021     },
38022
38023     getCollapseAnchor : function(){
38024         return this.canchors[this.position];
38025     },
38026
38027     getSlideAnchor : function(){
38028         return this.sanchors[this.position];
38029     },
38030
38031     getAlignAdj : function(){
38032         var cm = this.cmargins;
38033         switch(this.position){
38034             case "west":
38035                 return [0, 0];
38036             break;
38037             case "east":
38038                 return [0, 0];
38039             break;
38040             case "north":
38041                 return [0, 0];
38042             break;
38043             case "south":
38044                 return [0, 0];
38045             break;
38046         }
38047     },
38048
38049     getExpandAdj : function(){
38050         var c = this.collapsedEl, cm = this.cmargins;
38051         switch(this.position){
38052             case "west":
38053                 return [-(cm.right+c.getWidth()+cm.left), 0];
38054             break;
38055             case "east":
38056                 return [cm.right+c.getWidth()+cm.left, 0];
38057             break;
38058             case "north":
38059                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38060             break;
38061             case "south":
38062                 return [0, cm.top+cm.bottom+c.getHeight()];
38063             break;
38064         }
38065     }
38066 });/*
38067  * Based on:
38068  * Ext JS Library 1.1.1
38069  * Copyright(c) 2006-2007, Ext JS, LLC.
38070  *
38071  * Originally Released Under LGPL - original licence link has changed is not relivant.
38072  *
38073  * Fork - LGPL
38074  * <script type="text/javascript">
38075  */
38076 /*
38077  * These classes are private internal classes
38078  */
38079 Roo.bootstrap.layout.Center = function(config){
38080     config.region = "center";
38081     Roo.bootstrap.layout.Region.call(this, config);
38082     this.visible = true;
38083     this.minWidth = config.minWidth || 20;
38084     this.minHeight = config.minHeight || 20;
38085 };
38086
38087 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38088     hide : function(){
38089         // center panel can't be hidden
38090     },
38091     
38092     show : function(){
38093         // center panel can't be hidden
38094     },
38095     
38096     getMinWidth: function(){
38097         return this.minWidth;
38098     },
38099     
38100     getMinHeight: function(){
38101         return this.minHeight;
38102     }
38103 });
38104
38105
38106
38107
38108  
38109
38110
38111
38112
38113
38114
38115 Roo.bootstrap.layout.North = function(config)
38116 {
38117     config.region = 'north';
38118     config.cursor = 'n-resize';
38119     
38120     Roo.bootstrap.layout.Split.call(this, config);
38121     
38122     
38123     if(this.split){
38124         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38125         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38126         this.split.el.addClass("roo-layout-split-v");
38127     }
38128     var size = config.initialSize || config.height;
38129     if(typeof size != "undefined"){
38130         this.el.setHeight(size);
38131     }
38132 };
38133 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38134 {
38135     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38136     
38137     
38138     
38139     getBox : function(){
38140         if(this.collapsed){
38141             return this.collapsedEl.getBox();
38142         }
38143         var box = this.el.getBox();
38144         if(this.split){
38145             box.height += this.split.el.getHeight();
38146         }
38147         return box;
38148     },
38149     
38150     updateBox : function(box){
38151         if(this.split && !this.collapsed){
38152             box.height -= this.split.el.getHeight();
38153             this.split.el.setLeft(box.x);
38154             this.split.el.setTop(box.y+box.height);
38155             this.split.el.setWidth(box.width);
38156         }
38157         if(this.collapsed){
38158             this.updateBody(box.width, null);
38159         }
38160         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38161     }
38162 });
38163
38164
38165
38166
38167
38168 Roo.bootstrap.layout.South = function(config){
38169     config.region = 'south';
38170     config.cursor = 's-resize';
38171     Roo.bootstrap.layout.Split.call(this, config);
38172     if(this.split){
38173         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
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
38183 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38184     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38185     getBox : function(){
38186         if(this.collapsed){
38187             return this.collapsedEl.getBox();
38188         }
38189         var box = this.el.getBox();
38190         if(this.split){
38191             var sh = this.split.el.getHeight();
38192             box.height += sh;
38193             box.y -= sh;
38194         }
38195         return box;
38196     },
38197     
38198     updateBox : function(box){
38199         if(this.split && !this.collapsed){
38200             var sh = this.split.el.getHeight();
38201             box.height -= sh;
38202             box.y += sh;
38203             this.split.el.setLeft(box.x);
38204             this.split.el.setTop(box.y-sh);
38205             this.split.el.setWidth(box.width);
38206         }
38207         if(this.collapsed){
38208             this.updateBody(box.width, null);
38209         }
38210         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38211     }
38212 });
38213
38214 Roo.bootstrap.layout.East = function(config){
38215     config.region = "east";
38216     config.cursor = "e-resize";
38217     Roo.bootstrap.layout.Split.call(this, config);
38218     if(this.split){
38219         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38220         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38221         this.split.el.addClass("roo-layout-split-h");
38222     }
38223     var size = config.initialSize || config.width;
38224     if(typeof size != "undefined"){
38225         this.el.setWidth(size);
38226     }
38227 };
38228 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38229     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38230     getBox : function(){
38231         if(this.collapsed){
38232             return this.collapsedEl.getBox();
38233         }
38234         var box = this.el.getBox();
38235         if(this.split){
38236             var sw = this.split.el.getWidth();
38237             box.width += sw;
38238             box.x -= sw;
38239         }
38240         return box;
38241     },
38242
38243     updateBox : function(box){
38244         if(this.split && !this.collapsed){
38245             var sw = this.split.el.getWidth();
38246             box.width -= sw;
38247             this.split.el.setLeft(box.x);
38248             this.split.el.setTop(box.y);
38249             this.split.el.setHeight(box.height);
38250             box.x += sw;
38251         }
38252         if(this.collapsed){
38253             this.updateBody(null, box.height);
38254         }
38255         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38256     }
38257 });
38258
38259 Roo.bootstrap.layout.West = function(config){
38260     config.region = "west";
38261     config.cursor = "w-resize";
38262     
38263     Roo.bootstrap.layout.Split.call(this, config);
38264     if(this.split){
38265         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38266         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38267         this.split.el.addClass("roo-layout-split-h");
38268     }
38269     
38270 };
38271 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38272     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38273     
38274     onRender: function(ctr, pos)
38275     {
38276         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38277         var size = this.config.initialSize || this.config.width;
38278         if(typeof size != "undefined"){
38279             this.el.setWidth(size);
38280         }
38281     },
38282     
38283     getBox : function(){
38284         if(this.collapsed){
38285             return this.collapsedEl.getBox();
38286         }
38287         var box = this.el.getBox();
38288         if(this.split){
38289             box.width += this.split.el.getWidth();
38290         }
38291         return box;
38292     },
38293     
38294     updateBox : function(box){
38295         if(this.split && !this.collapsed){
38296             var sw = this.split.el.getWidth();
38297             box.width -= sw;
38298             this.split.el.setLeft(box.x+box.width);
38299             this.split.el.setTop(box.y);
38300             this.split.el.setHeight(box.height);
38301         }
38302         if(this.collapsed){
38303             this.updateBody(null, box.height);
38304         }
38305         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38306     }
38307 });Roo.namespace("Roo.bootstrap.panel");/*
38308  * Based on:
38309  * Ext JS Library 1.1.1
38310  * Copyright(c) 2006-2007, Ext JS, LLC.
38311  *
38312  * Originally Released Under LGPL - original licence link has changed is not relivant.
38313  *
38314  * Fork - LGPL
38315  * <script type="text/javascript">
38316  */
38317 /**
38318  * @class Roo.ContentPanel
38319  * @extends Roo.util.Observable
38320  * A basic ContentPanel element.
38321  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38322  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38323  * @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
38324  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38325  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38326  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38327  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38328  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38329  * @cfg {String} title          The title for this panel
38330  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38331  * @cfg {String} url            Calls {@link #setUrl} with this value
38332  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38333  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38334  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38335  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38336  * @cfg {Boolean} badges render the badges
38337
38338  * @constructor
38339  * Create a new ContentPanel.
38340  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38341  * @param {String/Object} config A string to set only the title or a config object
38342  * @param {String} content (optional) Set the HTML content for this panel
38343  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38344  */
38345 Roo.bootstrap.panel.Content = function( config){
38346     
38347     this.tpl = config.tpl || false;
38348     
38349     var el = config.el;
38350     var content = config.content;
38351
38352     if(config.autoCreate){ // xtype is available if this is called from factory
38353         el = Roo.id();
38354     }
38355     this.el = Roo.get(el);
38356     if(!this.el && config && config.autoCreate){
38357         if(typeof config.autoCreate == "object"){
38358             if(!config.autoCreate.id){
38359                 config.autoCreate.id = config.id||el;
38360             }
38361             this.el = Roo.DomHelper.append(document.body,
38362                         config.autoCreate, true);
38363         }else{
38364             var elcfg =  {   tag: "div",
38365                             cls: "roo-layout-inactive-content",
38366                             id: config.id||el
38367                             };
38368             if (config.html) {
38369                 elcfg.html = config.html;
38370                 
38371             }
38372                         
38373             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38374         }
38375     } 
38376     this.closable = false;
38377     this.loaded = false;
38378     this.active = false;
38379    
38380       
38381     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38382         
38383         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38384         
38385         this.wrapEl = this.el; //this.el.wrap();
38386         var ti = [];
38387         if (config.toolbar.items) {
38388             ti = config.toolbar.items ;
38389             delete config.toolbar.items ;
38390         }
38391         
38392         var nitems = [];
38393         this.toolbar.render(this.wrapEl, 'before');
38394         for(var i =0;i < ti.length;i++) {
38395           //  Roo.log(['add child', items[i]]);
38396             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38397         }
38398         this.toolbar.items = nitems;
38399         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38400         delete config.toolbar;
38401         
38402     }
38403     /*
38404     // xtype created footer. - not sure if will work as we normally have to render first..
38405     if (this.footer && !this.footer.el && this.footer.xtype) {
38406         if (!this.wrapEl) {
38407             this.wrapEl = this.el.wrap();
38408         }
38409     
38410         this.footer.container = this.wrapEl.createChild();
38411          
38412         this.footer = Roo.factory(this.footer, Roo);
38413         
38414     }
38415     */
38416     
38417      if(typeof config == "string"){
38418         this.title = config;
38419     }else{
38420         Roo.apply(this, config);
38421     }
38422     
38423     if(this.resizeEl){
38424         this.resizeEl = Roo.get(this.resizeEl, true);
38425     }else{
38426         this.resizeEl = this.el;
38427     }
38428     // handle view.xtype
38429     
38430  
38431     
38432     
38433     this.addEvents({
38434         /**
38435          * @event activate
38436          * Fires when this panel is activated. 
38437          * @param {Roo.ContentPanel} this
38438          */
38439         "activate" : true,
38440         /**
38441          * @event deactivate
38442          * Fires when this panel is activated. 
38443          * @param {Roo.ContentPanel} this
38444          */
38445         "deactivate" : true,
38446
38447         /**
38448          * @event resize
38449          * Fires when this panel is resized if fitToFrame is true.
38450          * @param {Roo.ContentPanel} this
38451          * @param {Number} width The width after any component adjustments
38452          * @param {Number} height The height after any component adjustments
38453          */
38454         "resize" : true,
38455         
38456          /**
38457          * @event render
38458          * Fires when this tab is created
38459          * @param {Roo.ContentPanel} this
38460          */
38461         "render" : true
38462         
38463         
38464         
38465     });
38466     
38467
38468     
38469     
38470     if(this.autoScroll){
38471         this.resizeEl.setStyle("overflow", "auto");
38472     } else {
38473         // fix randome scrolling
38474         //this.el.on('scroll', function() {
38475         //    Roo.log('fix random scolling');
38476         //    this.scrollTo('top',0); 
38477         //});
38478     }
38479     content = content || this.content;
38480     if(content){
38481         this.setContent(content);
38482     }
38483     if(config && config.url){
38484         this.setUrl(this.url, this.params, this.loadOnce);
38485     }
38486     
38487     
38488     
38489     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38490     
38491     if (this.view && typeof(this.view.xtype) != 'undefined') {
38492         this.view.el = this.el.appendChild(document.createElement("div"));
38493         this.view = Roo.factory(this.view); 
38494         this.view.render  &&  this.view.render(false, '');  
38495     }
38496     
38497     
38498     this.fireEvent('render', this);
38499 };
38500
38501 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38502     
38503     tabTip : '',
38504     
38505     setRegion : function(region){
38506         this.region = region;
38507         this.setActiveClass(region && !this.background);
38508     },
38509     
38510     
38511     setActiveClass: function(state)
38512     {
38513         if(state){
38514            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38515            this.el.setStyle('position','relative');
38516         }else{
38517            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38518            this.el.setStyle('position', 'absolute');
38519         } 
38520     },
38521     
38522     /**
38523      * Returns the toolbar for this Panel if one was configured. 
38524      * @return {Roo.Toolbar} 
38525      */
38526     getToolbar : function(){
38527         return this.toolbar;
38528     },
38529     
38530     setActiveState : function(active)
38531     {
38532         this.active = active;
38533         this.setActiveClass(active);
38534         if(!active){
38535             if(this.fireEvent("deactivate", this) === false){
38536                 return false;
38537             }
38538             return true;
38539         }
38540         this.fireEvent("activate", this);
38541         return true;
38542     },
38543     /**
38544      * Updates this panel's element
38545      * @param {String} content The new content
38546      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38547     */
38548     setContent : function(content, loadScripts){
38549         this.el.update(content, loadScripts);
38550     },
38551
38552     ignoreResize : function(w, h){
38553         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38554             return true;
38555         }else{
38556             this.lastSize = {width: w, height: h};
38557             return false;
38558         }
38559     },
38560     /**
38561      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38562      * @return {Roo.UpdateManager} The UpdateManager
38563      */
38564     getUpdateManager : function(){
38565         return this.el.getUpdateManager();
38566     },
38567      /**
38568      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38569      * @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:
38570 <pre><code>
38571 panel.load({
38572     url: "your-url.php",
38573     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38574     callback: yourFunction,
38575     scope: yourObject, //(optional scope)
38576     discardUrl: false,
38577     nocache: false,
38578     text: "Loading...",
38579     timeout: 30,
38580     scripts: false
38581 });
38582 </code></pre>
38583      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38584      * 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.
38585      * @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}
38586      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38587      * @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.
38588      * @return {Roo.ContentPanel} this
38589      */
38590     load : function(){
38591         var um = this.el.getUpdateManager();
38592         um.update.apply(um, arguments);
38593         return this;
38594     },
38595
38596
38597     /**
38598      * 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.
38599      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38600      * @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)
38601      * @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)
38602      * @return {Roo.UpdateManager} The UpdateManager
38603      */
38604     setUrl : function(url, params, loadOnce){
38605         if(this.refreshDelegate){
38606             this.removeListener("activate", this.refreshDelegate);
38607         }
38608         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38609         this.on("activate", this.refreshDelegate);
38610         return this.el.getUpdateManager();
38611     },
38612     
38613     _handleRefresh : function(url, params, loadOnce){
38614         if(!loadOnce || !this.loaded){
38615             var updater = this.el.getUpdateManager();
38616             updater.update(url, params, this._setLoaded.createDelegate(this));
38617         }
38618     },
38619     
38620     _setLoaded : function(){
38621         this.loaded = true;
38622     }, 
38623     
38624     /**
38625      * Returns this panel's id
38626      * @return {String} 
38627      */
38628     getId : function(){
38629         return this.el.id;
38630     },
38631     
38632     /** 
38633      * Returns this panel's element - used by regiosn to add.
38634      * @return {Roo.Element} 
38635      */
38636     getEl : function(){
38637         return this.wrapEl || this.el;
38638     },
38639     
38640    
38641     
38642     adjustForComponents : function(width, height)
38643     {
38644         //Roo.log('adjustForComponents ');
38645         if(this.resizeEl != this.el){
38646             width -= this.el.getFrameWidth('lr');
38647             height -= this.el.getFrameWidth('tb');
38648         }
38649         if(this.toolbar){
38650             var te = this.toolbar.getEl();
38651             te.setWidth(width);
38652             height -= te.getHeight();
38653         }
38654         if(this.footer){
38655             var te = this.footer.getEl();
38656             te.setWidth(width);
38657             height -= te.getHeight();
38658         }
38659         
38660         
38661         if(this.adjustments){
38662             width += this.adjustments[0];
38663             height += this.adjustments[1];
38664         }
38665         return {"width": width, "height": height};
38666     },
38667     
38668     setSize : function(width, height){
38669         if(this.fitToFrame && !this.ignoreResize(width, height)){
38670             if(this.fitContainer && this.resizeEl != this.el){
38671                 this.el.setSize(width, height);
38672             }
38673             var size = this.adjustForComponents(width, height);
38674             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38675             this.fireEvent('resize', this, size.width, size.height);
38676         }
38677     },
38678     
38679     /**
38680      * Returns this panel's title
38681      * @return {String} 
38682      */
38683     getTitle : function(){
38684         
38685         if (typeof(this.title) != 'object') {
38686             return this.title;
38687         }
38688         
38689         var t = '';
38690         for (var k in this.title) {
38691             if (!this.title.hasOwnProperty(k)) {
38692                 continue;
38693             }
38694             
38695             if (k.indexOf('-') >= 0) {
38696                 var s = k.split('-');
38697                 for (var i = 0; i<s.length; i++) {
38698                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38699                 }
38700             } else {
38701                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38702             }
38703         }
38704         return t;
38705     },
38706     
38707     /**
38708      * Set this panel's title
38709      * @param {String} title
38710      */
38711     setTitle : function(title){
38712         this.title = title;
38713         if(this.region){
38714             this.region.updatePanelTitle(this, title);
38715         }
38716     },
38717     
38718     /**
38719      * Returns true is this panel was configured to be closable
38720      * @return {Boolean} 
38721      */
38722     isClosable : function(){
38723         return this.closable;
38724     },
38725     
38726     beforeSlide : function(){
38727         this.el.clip();
38728         this.resizeEl.clip();
38729     },
38730     
38731     afterSlide : function(){
38732         this.el.unclip();
38733         this.resizeEl.unclip();
38734     },
38735     
38736     /**
38737      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38738      *   Will fail silently if the {@link #setUrl} method has not been called.
38739      *   This does not activate the panel, just updates its content.
38740      */
38741     refresh : function(){
38742         if(this.refreshDelegate){
38743            this.loaded = false;
38744            this.refreshDelegate();
38745         }
38746     },
38747     
38748     /**
38749      * Destroys this panel
38750      */
38751     destroy : function(){
38752         this.el.removeAllListeners();
38753         var tempEl = document.createElement("span");
38754         tempEl.appendChild(this.el.dom);
38755         tempEl.innerHTML = "";
38756         this.el.remove();
38757         this.el = null;
38758     },
38759     
38760     /**
38761      * form - if the content panel contains a form - this is a reference to it.
38762      * @type {Roo.form.Form}
38763      */
38764     form : false,
38765     /**
38766      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38767      *    This contains a reference to it.
38768      * @type {Roo.View}
38769      */
38770     view : false,
38771     
38772       /**
38773      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38774      * <pre><code>
38775
38776 layout.addxtype({
38777        xtype : 'Form',
38778        items: [ .... ]
38779    }
38780 );
38781
38782 </code></pre>
38783      * @param {Object} cfg Xtype definition of item to add.
38784      */
38785     
38786     
38787     getChildContainer: function () {
38788         return this.getEl();
38789     }
38790     
38791     
38792     /*
38793         var  ret = new Roo.factory(cfg);
38794         return ret;
38795         
38796         
38797         // add form..
38798         if (cfg.xtype.match(/^Form$/)) {
38799             
38800             var el;
38801             //if (this.footer) {
38802             //    el = this.footer.container.insertSibling(false, 'before');
38803             //} else {
38804                 el = this.el.createChild();
38805             //}
38806
38807             this.form = new  Roo.form.Form(cfg);
38808             
38809             
38810             if ( this.form.allItems.length) {
38811                 this.form.render(el.dom);
38812             }
38813             return this.form;
38814         }
38815         // should only have one of theses..
38816         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38817             // views.. should not be just added - used named prop 'view''
38818             
38819             cfg.el = this.el.appendChild(document.createElement("div"));
38820             // factory?
38821             
38822             var ret = new Roo.factory(cfg);
38823              
38824              ret.render && ret.render(false, ''); // render blank..
38825             this.view = ret;
38826             return ret;
38827         }
38828         return false;
38829     }
38830     \*/
38831 });
38832  
38833 /**
38834  * @class Roo.bootstrap.panel.Grid
38835  * @extends Roo.bootstrap.panel.Content
38836  * @constructor
38837  * Create a new GridPanel.
38838  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38839  * @param {Object} config A the config object
38840   
38841  */
38842
38843
38844
38845 Roo.bootstrap.panel.Grid = function(config)
38846 {
38847     
38848       
38849     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38850         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38851
38852     config.el = this.wrapper;
38853     //this.el = this.wrapper;
38854     
38855       if (config.container) {
38856         // ctor'ed from a Border/panel.grid
38857         
38858         
38859         this.wrapper.setStyle("overflow", "hidden");
38860         this.wrapper.addClass('roo-grid-container');
38861
38862     }
38863     
38864     
38865     if(config.toolbar){
38866         var tool_el = this.wrapper.createChild();    
38867         this.toolbar = Roo.factory(config.toolbar);
38868         var ti = [];
38869         if (config.toolbar.items) {
38870             ti = config.toolbar.items ;
38871             delete config.toolbar.items ;
38872         }
38873         
38874         var nitems = [];
38875         this.toolbar.render(tool_el);
38876         for(var i =0;i < ti.length;i++) {
38877           //  Roo.log(['add child', items[i]]);
38878             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38879         }
38880         this.toolbar.items = nitems;
38881         
38882         delete config.toolbar;
38883     }
38884     
38885     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38886     config.grid.scrollBody = true;;
38887     config.grid.monitorWindowResize = false; // turn off autosizing
38888     config.grid.autoHeight = false;
38889     config.grid.autoWidth = false;
38890     
38891     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38892     
38893     if (config.background) {
38894         // render grid on panel activation (if panel background)
38895         this.on('activate', function(gp) {
38896             if (!gp.grid.rendered) {
38897                 gp.grid.render(this.wrapper);
38898                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38899             }
38900         });
38901             
38902     } else {
38903         this.grid.render(this.wrapper);
38904         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38905
38906     }
38907     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38908     // ??? needed ??? config.el = this.wrapper;
38909     
38910     
38911     
38912   
38913     // xtype created footer. - not sure if will work as we normally have to render first..
38914     if (this.footer && !this.footer.el && this.footer.xtype) {
38915         
38916         var ctr = this.grid.getView().getFooterPanel(true);
38917         this.footer.dataSource = this.grid.dataSource;
38918         this.footer = Roo.factory(this.footer, Roo);
38919         this.footer.render(ctr);
38920         
38921     }
38922     
38923     
38924     
38925     
38926      
38927 };
38928
38929 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38930     getId : function(){
38931         return this.grid.id;
38932     },
38933     
38934     /**
38935      * Returns the grid for this panel
38936      * @return {Roo.bootstrap.Table} 
38937      */
38938     getGrid : function(){
38939         return this.grid;    
38940     },
38941     
38942     setSize : function(width, height){
38943         if(!this.ignoreResize(width, height)){
38944             var grid = this.grid;
38945             var size = this.adjustForComponents(width, height);
38946             var gridel = grid.getGridEl();
38947             gridel.setSize(size.width, size.height);
38948             /*
38949             var thd = grid.getGridEl().select('thead',true).first();
38950             var tbd = grid.getGridEl().select('tbody', true).first();
38951             if (tbd) {
38952                 tbd.setSize(width, height - thd.getHeight());
38953             }
38954             */
38955             grid.autoSize();
38956         }
38957     },
38958      
38959     
38960     
38961     beforeSlide : function(){
38962         this.grid.getView().scroller.clip();
38963     },
38964     
38965     afterSlide : function(){
38966         this.grid.getView().scroller.unclip();
38967     },
38968     
38969     destroy : function(){
38970         this.grid.destroy();
38971         delete this.grid;
38972         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38973     }
38974 });
38975
38976 /**
38977  * @class Roo.bootstrap.panel.Nest
38978  * @extends Roo.bootstrap.panel.Content
38979  * @constructor
38980  * Create a new Panel, that can contain a layout.Border.
38981  * 
38982  * 
38983  * @param {Roo.BorderLayout} layout The layout for this panel
38984  * @param {String/Object} config A string to set only the title or a config object
38985  */
38986 Roo.bootstrap.panel.Nest = function(config)
38987 {
38988     // construct with only one argument..
38989     /* FIXME - implement nicer consturctors
38990     if (layout.layout) {
38991         config = layout;
38992         layout = config.layout;
38993         delete config.layout;
38994     }
38995     if (layout.xtype && !layout.getEl) {
38996         // then layout needs constructing..
38997         layout = Roo.factory(layout, Roo);
38998     }
38999     */
39000     
39001     config.el =  config.layout.getEl();
39002     
39003     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39004     
39005     config.layout.monitorWindowResize = false; // turn off autosizing
39006     this.layout = config.layout;
39007     this.layout.getEl().addClass("roo-layout-nested-layout");
39008     this.layout.parent = this;
39009     
39010     
39011     
39012     
39013 };
39014
39015 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39016
39017     setSize : function(width, height){
39018         if(!this.ignoreResize(width, height)){
39019             var size = this.adjustForComponents(width, height);
39020             var el = this.layout.getEl();
39021             if (size.height < 1) {
39022                 el.setWidth(size.width);   
39023             } else {
39024                 el.setSize(size.width, size.height);
39025             }
39026             var touch = el.dom.offsetWidth;
39027             this.layout.layout();
39028             // ie requires a double layout on the first pass
39029             if(Roo.isIE && !this.initialized){
39030                 this.initialized = true;
39031                 this.layout.layout();
39032             }
39033         }
39034     },
39035     
39036     // activate all subpanels if not currently active..
39037     
39038     setActiveState : function(active){
39039         this.active = active;
39040         this.setActiveClass(active);
39041         
39042         if(!active){
39043             this.fireEvent("deactivate", this);
39044             return;
39045         }
39046         
39047         this.fireEvent("activate", this);
39048         // not sure if this should happen before or after..
39049         if (!this.layout) {
39050             return; // should not happen..
39051         }
39052         var reg = false;
39053         for (var r in this.layout.regions) {
39054             reg = this.layout.getRegion(r);
39055             if (reg.getActivePanel()) {
39056                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39057                 reg.setActivePanel(reg.getActivePanel());
39058                 continue;
39059             }
39060             if (!reg.panels.length) {
39061                 continue;
39062             }
39063             reg.showPanel(reg.getPanel(0));
39064         }
39065         
39066         
39067         
39068         
39069     },
39070     
39071     /**
39072      * Returns the nested BorderLayout for this panel
39073      * @return {Roo.BorderLayout} 
39074      */
39075     getLayout : function(){
39076         return this.layout;
39077     },
39078     
39079      /**
39080      * Adds a xtype elements to the layout of the nested panel
39081      * <pre><code>
39082
39083 panel.addxtype({
39084        xtype : 'ContentPanel',
39085        region: 'west',
39086        items: [ .... ]
39087    }
39088 );
39089
39090 panel.addxtype({
39091         xtype : 'NestedLayoutPanel',
39092         region: 'west',
39093         layout: {
39094            center: { },
39095            west: { }   
39096         },
39097         items : [ ... list of content panels or nested layout panels.. ]
39098    }
39099 );
39100 </code></pre>
39101      * @param {Object} cfg Xtype definition of item to add.
39102      */
39103     addxtype : function(cfg) {
39104         return this.layout.addxtype(cfg);
39105     
39106     }
39107 });/*
39108  * Based on:
39109  * Ext JS Library 1.1.1
39110  * Copyright(c) 2006-2007, Ext JS, LLC.
39111  *
39112  * Originally Released Under LGPL - original licence link has changed is not relivant.
39113  *
39114  * Fork - LGPL
39115  * <script type="text/javascript">
39116  */
39117 /**
39118  * @class Roo.TabPanel
39119  * @extends Roo.util.Observable
39120  * A lightweight tab container.
39121  * <br><br>
39122  * Usage:
39123  * <pre><code>
39124 // basic tabs 1, built from existing content
39125 var tabs = new Roo.TabPanel("tabs1");
39126 tabs.addTab("script", "View Script");
39127 tabs.addTab("markup", "View Markup");
39128 tabs.activate("script");
39129
39130 // more advanced tabs, built from javascript
39131 var jtabs = new Roo.TabPanel("jtabs");
39132 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39133
39134 // set up the UpdateManager
39135 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39136 var updater = tab2.getUpdateManager();
39137 updater.setDefaultUrl("ajax1.htm");
39138 tab2.on('activate', updater.refresh, updater, true);
39139
39140 // Use setUrl for Ajax loading
39141 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39142 tab3.setUrl("ajax2.htm", null, true);
39143
39144 // Disabled tab
39145 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39146 tab4.disable();
39147
39148 jtabs.activate("jtabs-1");
39149  * </code></pre>
39150  * @constructor
39151  * Create a new TabPanel.
39152  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39153  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39154  */
39155 Roo.bootstrap.panel.Tabs = function(config){
39156     /**
39157     * The container element for this TabPanel.
39158     * @type Roo.Element
39159     */
39160     this.el = Roo.get(config.el);
39161     delete config.el;
39162     if(config){
39163         if(typeof config == "boolean"){
39164             this.tabPosition = config ? "bottom" : "top";
39165         }else{
39166             Roo.apply(this, config);
39167         }
39168     }
39169     
39170     if(this.tabPosition == "bottom"){
39171         // if tabs are at the bottom = create the body first.
39172         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39173         this.el.addClass("roo-tabs-bottom");
39174     }
39175     // next create the tabs holders
39176     
39177     if (this.tabPosition == "west"){
39178         
39179         var reg = this.region; // fake it..
39180         while (reg) {
39181             if (!reg.mgr.parent) {
39182                 break;
39183             }
39184             reg = reg.mgr.parent.region;
39185         }
39186         Roo.log("got nest?");
39187         Roo.log(reg);
39188         if (reg.mgr.getRegion('west')) {
39189             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39190             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39191             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39192             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39193             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39194         
39195             
39196         }
39197         
39198         
39199     } else {
39200      
39201         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39202         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39203         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39204         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39205     }
39206     
39207     
39208     if(Roo.isIE){
39209         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39210     }
39211     
39212     // finally - if tabs are at the top, then create the body last..
39213     if(this.tabPosition != "bottom"){
39214         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39215          * @type Roo.Element
39216          */
39217         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39218         this.el.addClass("roo-tabs-top");
39219     }
39220     this.items = [];
39221
39222     this.bodyEl.setStyle("position", "relative");
39223
39224     this.active = null;
39225     this.activateDelegate = this.activate.createDelegate(this);
39226
39227     this.addEvents({
39228         /**
39229          * @event tabchange
39230          * Fires when the active tab changes
39231          * @param {Roo.TabPanel} this
39232          * @param {Roo.TabPanelItem} activePanel The new active tab
39233          */
39234         "tabchange": true,
39235         /**
39236          * @event beforetabchange
39237          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39238          * @param {Roo.TabPanel} this
39239          * @param {Object} e Set cancel to true on this object to cancel the tab change
39240          * @param {Roo.TabPanelItem} tab The tab being changed to
39241          */
39242         "beforetabchange" : true
39243     });
39244
39245     Roo.EventManager.onWindowResize(this.onResize, this);
39246     this.cpad = this.el.getPadding("lr");
39247     this.hiddenCount = 0;
39248
39249
39250     // toolbar on the tabbar support...
39251     if (this.toolbar) {
39252         alert("no toolbar support yet");
39253         this.toolbar  = false;
39254         /*
39255         var tcfg = this.toolbar;
39256         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39257         this.toolbar = new Roo.Toolbar(tcfg);
39258         if (Roo.isSafari) {
39259             var tbl = tcfg.container.child('table', true);
39260             tbl.setAttribute('width', '100%');
39261         }
39262         */
39263         
39264     }
39265    
39266
39267
39268     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39269 };
39270
39271 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39272     /*
39273      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39274      */
39275     tabPosition : "top",
39276     /*
39277      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39278      */
39279     currentTabWidth : 0,
39280     /*
39281      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39282      */
39283     minTabWidth : 40,
39284     /*
39285      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39286      */
39287     maxTabWidth : 250,
39288     /*
39289      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39290      */
39291     preferredTabWidth : 175,
39292     /*
39293      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39294      */
39295     resizeTabs : false,
39296     /*
39297      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39298      */
39299     monitorResize : true,
39300     /*
39301      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39302      */
39303     toolbar : false,  // set by caller..
39304     
39305     region : false, /// set by caller
39306     
39307     disableTooltips : true, // not used yet...
39308
39309     /**
39310      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39311      * @param {String} id The id of the div to use <b>or create</b>
39312      * @param {String} text The text for the tab
39313      * @param {String} content (optional) Content to put in the TabPanelItem body
39314      * @param {Boolean} closable (optional) True to create a close icon on the tab
39315      * @return {Roo.TabPanelItem} The created TabPanelItem
39316      */
39317     addTab : function(id, text, content, closable, tpl)
39318     {
39319         var item = new Roo.bootstrap.panel.TabItem({
39320             panel: this,
39321             id : id,
39322             text : text,
39323             closable : closable,
39324             tpl : tpl
39325         });
39326         this.addTabItem(item);
39327         if(content){
39328             item.setContent(content);
39329         }
39330         return item;
39331     },
39332
39333     /**
39334      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39335      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39336      * @return {Roo.TabPanelItem}
39337      */
39338     getTab : function(id){
39339         return this.items[id];
39340     },
39341
39342     /**
39343      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39344      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39345      */
39346     hideTab : function(id){
39347         var t = this.items[id];
39348         if(!t.isHidden()){
39349            t.setHidden(true);
39350            this.hiddenCount++;
39351            this.autoSizeTabs();
39352         }
39353     },
39354
39355     /**
39356      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39357      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39358      */
39359     unhideTab : function(id){
39360         var t = this.items[id];
39361         if(t.isHidden()){
39362            t.setHidden(false);
39363            this.hiddenCount--;
39364            this.autoSizeTabs();
39365         }
39366     },
39367
39368     /**
39369      * Adds an existing {@link Roo.TabPanelItem}.
39370      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39371      */
39372     addTabItem : function(item)
39373     {
39374         this.items[item.id] = item;
39375         this.items.push(item);
39376         this.autoSizeTabs();
39377       //  if(this.resizeTabs){
39378     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39379   //         this.autoSizeTabs();
39380 //        }else{
39381 //            item.autoSize();
39382        // }
39383     },
39384
39385     /**
39386      * Removes a {@link Roo.TabPanelItem}.
39387      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39388      */
39389     removeTab : function(id){
39390         var items = this.items;
39391         var tab = items[id];
39392         if(!tab) { return; }
39393         var index = items.indexOf(tab);
39394         if(this.active == tab && items.length > 1){
39395             var newTab = this.getNextAvailable(index);
39396             if(newTab) {
39397                 newTab.activate();
39398             }
39399         }
39400         this.stripEl.dom.removeChild(tab.pnode.dom);
39401         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39402             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39403         }
39404         items.splice(index, 1);
39405         delete this.items[tab.id];
39406         tab.fireEvent("close", tab);
39407         tab.purgeListeners();
39408         this.autoSizeTabs();
39409     },
39410
39411     getNextAvailable : function(start){
39412         var items = this.items;
39413         var index = start;
39414         // look for a next tab that will slide over to
39415         // replace the one being removed
39416         while(index < items.length){
39417             var item = items[++index];
39418             if(item && !item.isHidden()){
39419                 return item;
39420             }
39421         }
39422         // if one isn't found select the previous tab (on the left)
39423         index = start;
39424         while(index >= 0){
39425             var item = items[--index];
39426             if(item && !item.isHidden()){
39427                 return item;
39428             }
39429         }
39430         return null;
39431     },
39432
39433     /**
39434      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39435      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39436      */
39437     disableTab : function(id){
39438         var tab = this.items[id];
39439         if(tab && this.active != tab){
39440             tab.disable();
39441         }
39442     },
39443
39444     /**
39445      * Enables a {@link Roo.TabPanelItem} that is disabled.
39446      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39447      */
39448     enableTab : function(id){
39449         var tab = this.items[id];
39450         tab.enable();
39451     },
39452
39453     /**
39454      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39455      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39456      * @return {Roo.TabPanelItem} The TabPanelItem.
39457      */
39458     activate : function(id)
39459     {
39460         //Roo.log('activite:'  + id);
39461         
39462         var tab = this.items[id];
39463         if(!tab){
39464             return null;
39465         }
39466         if(tab == this.active || tab.disabled){
39467             return tab;
39468         }
39469         var e = {};
39470         this.fireEvent("beforetabchange", this, e, tab);
39471         if(e.cancel !== true && !tab.disabled){
39472             if(this.active){
39473                 this.active.hide();
39474             }
39475             this.active = this.items[id];
39476             this.active.show();
39477             this.fireEvent("tabchange", this, this.active);
39478         }
39479         return tab;
39480     },
39481
39482     /**
39483      * Gets the active {@link Roo.TabPanelItem}.
39484      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39485      */
39486     getActiveTab : function(){
39487         return this.active;
39488     },
39489
39490     /**
39491      * Updates the tab body element to fit the height of the container element
39492      * for overflow scrolling
39493      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39494      */
39495     syncHeight : function(targetHeight){
39496         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39497         var bm = this.bodyEl.getMargins();
39498         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39499         this.bodyEl.setHeight(newHeight);
39500         return newHeight;
39501     },
39502
39503     onResize : function(){
39504         if(this.monitorResize){
39505             this.autoSizeTabs();
39506         }
39507     },
39508
39509     /**
39510      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39511      */
39512     beginUpdate : function(){
39513         this.updating = true;
39514     },
39515
39516     /**
39517      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39518      */
39519     endUpdate : function(){
39520         this.updating = false;
39521         this.autoSizeTabs();
39522     },
39523
39524     /**
39525      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39526      */
39527     autoSizeTabs : function()
39528     {
39529         var count = this.items.length;
39530         var vcount = count - this.hiddenCount;
39531         
39532         if (vcount < 2) {
39533             this.stripEl.hide();
39534         } else {
39535             this.stripEl.show();
39536         }
39537         
39538         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39539             return;
39540         }
39541         
39542         
39543         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39544         var availWidth = Math.floor(w / vcount);
39545         var b = this.stripBody;
39546         if(b.getWidth() > w){
39547             var tabs = this.items;
39548             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39549             if(availWidth < this.minTabWidth){
39550                 /*if(!this.sleft){    // incomplete scrolling code
39551                     this.createScrollButtons();
39552                 }
39553                 this.showScroll();
39554                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39555             }
39556         }else{
39557             if(this.currentTabWidth < this.preferredTabWidth){
39558                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39559             }
39560         }
39561     },
39562
39563     /**
39564      * Returns the number of tabs in this TabPanel.
39565      * @return {Number}
39566      */
39567      getCount : function(){
39568          return this.items.length;
39569      },
39570
39571     /**
39572      * Resizes all the tabs to the passed width
39573      * @param {Number} The new width
39574      */
39575     setTabWidth : function(width){
39576         this.currentTabWidth = width;
39577         for(var i = 0, len = this.items.length; i < len; i++) {
39578                 if(!this.items[i].isHidden()) {
39579                 this.items[i].setWidth(width);
39580             }
39581         }
39582     },
39583
39584     /**
39585      * Destroys this TabPanel
39586      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39587      */
39588     destroy : function(removeEl){
39589         Roo.EventManager.removeResizeListener(this.onResize, this);
39590         for(var i = 0, len = this.items.length; i < len; i++){
39591             this.items[i].purgeListeners();
39592         }
39593         if(removeEl === true){
39594             this.el.update("");
39595             this.el.remove();
39596         }
39597     },
39598     
39599     createStrip : function(container)
39600     {
39601         var strip = document.createElement("nav");
39602         strip.className = Roo.bootstrap.version == 4 ?
39603             "navbar-light bg-light" : 
39604             "navbar navbar-default"; //"x-tabs-wrap";
39605         container.appendChild(strip);
39606         return strip;
39607     },
39608     
39609     createStripList : function(strip)
39610     {
39611         // div wrapper for retard IE
39612         // returns the "tr" element.
39613         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39614         //'<div class="x-tabs-strip-wrap">'+
39615           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39616           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39617         return strip.firstChild; //.firstChild.firstChild.firstChild;
39618     },
39619     createBody : function(container)
39620     {
39621         var body = document.createElement("div");
39622         Roo.id(body, "tab-body");
39623         //Roo.fly(body).addClass("x-tabs-body");
39624         Roo.fly(body).addClass("tab-content");
39625         container.appendChild(body);
39626         return body;
39627     },
39628     createItemBody :function(bodyEl, id){
39629         var body = Roo.getDom(id);
39630         if(!body){
39631             body = document.createElement("div");
39632             body.id = id;
39633         }
39634         //Roo.fly(body).addClass("x-tabs-item-body");
39635         Roo.fly(body).addClass("tab-pane");
39636          bodyEl.insertBefore(body, bodyEl.firstChild);
39637         return body;
39638     },
39639     /** @private */
39640     createStripElements :  function(stripEl, text, closable, tpl)
39641     {
39642         var td = document.createElement("li"); // was td..
39643         td.className = 'nav-item';
39644         
39645         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39646         
39647         
39648         stripEl.appendChild(td);
39649         /*if(closable){
39650             td.className = "x-tabs-closable";
39651             if(!this.closeTpl){
39652                 this.closeTpl = new Roo.Template(
39653                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39654                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39655                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39656                 );
39657             }
39658             var el = this.closeTpl.overwrite(td, {"text": text});
39659             var close = el.getElementsByTagName("div")[0];
39660             var inner = el.getElementsByTagName("em")[0];
39661             return {"el": el, "close": close, "inner": inner};
39662         } else {
39663         */
39664         // not sure what this is..
39665 //            if(!this.tabTpl){
39666                 //this.tabTpl = new Roo.Template(
39667                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39668                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39669                 //);
39670 //                this.tabTpl = new Roo.Template(
39671 //                   '<a href="#">' +
39672 //                   '<span unselectable="on"' +
39673 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39674 //                            ' >{text}</span></a>'
39675 //                );
39676 //                
39677 //            }
39678
39679
39680             var template = tpl || this.tabTpl || false;
39681             
39682             if(!template){
39683                 template =  new Roo.Template(
39684                         Roo.bootstrap.version == 4 ? 
39685                             (
39686                                 '<a class="nav-link" href="#" unselectable="on"' +
39687                                      (this.disableTooltips ? '' : ' title="{text}"') +
39688                                      ' >{text}</a>'
39689                             ) : (
39690                                 '<a class="nav-link" href="#">' +
39691                                 '<span unselectable="on"' +
39692                                          (this.disableTooltips ? '' : ' title="{text}"') +
39693                                     ' >{text}</span></a>'
39694                             )
39695                 );
39696             }
39697             
39698             switch (typeof(template)) {
39699                 case 'object' :
39700                     break;
39701                 case 'string' :
39702                     template = new Roo.Template(template);
39703                     break;
39704                 default :
39705                     break;
39706             }
39707             
39708             var el = template.overwrite(td, {"text": text});
39709             
39710             var inner = el.getElementsByTagName("span")[0];
39711             
39712             return {"el": el, "inner": inner};
39713             
39714     }
39715         
39716     
39717 });
39718
39719 /**
39720  * @class Roo.TabPanelItem
39721  * @extends Roo.util.Observable
39722  * Represents an individual item (tab plus body) in a TabPanel.
39723  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39724  * @param {String} id The id of this TabPanelItem
39725  * @param {String} text The text for the tab of this TabPanelItem
39726  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39727  */
39728 Roo.bootstrap.panel.TabItem = function(config){
39729     /**
39730      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39731      * @type Roo.TabPanel
39732      */
39733     this.tabPanel = config.panel;
39734     /**
39735      * The id for this TabPanelItem
39736      * @type String
39737      */
39738     this.id = config.id;
39739     /** @private */
39740     this.disabled = false;
39741     /** @private */
39742     this.text = config.text;
39743     /** @private */
39744     this.loaded = false;
39745     this.closable = config.closable;
39746
39747     /**
39748      * The body element for this TabPanelItem.
39749      * @type Roo.Element
39750      */
39751     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39752     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39753     this.bodyEl.setStyle("display", "block");
39754     this.bodyEl.setStyle("zoom", "1");
39755     //this.hideAction();
39756
39757     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39758     /** @private */
39759     this.el = Roo.get(els.el);
39760     this.inner = Roo.get(els.inner, true);
39761      this.textEl = Roo.bootstrap.version == 4 ?
39762         this.el : Roo.get(this.el.dom.firstChild, true);
39763
39764     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39765     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39766
39767     
39768 //    this.el.on("mousedown", this.onTabMouseDown, this);
39769     this.el.on("click", this.onTabClick, this);
39770     /** @private */
39771     if(config.closable){
39772         var c = Roo.get(els.close, true);
39773         c.dom.title = this.closeText;
39774         c.addClassOnOver("close-over");
39775         c.on("click", this.closeClick, this);
39776      }
39777
39778     this.addEvents({
39779          /**
39780          * @event activate
39781          * Fires when this tab becomes the active tab.
39782          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39783          * @param {Roo.TabPanelItem} this
39784          */
39785         "activate": true,
39786         /**
39787          * @event beforeclose
39788          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39789          * @param {Roo.TabPanelItem} this
39790          * @param {Object} e Set cancel to true on this object to cancel the close.
39791          */
39792         "beforeclose": true,
39793         /**
39794          * @event close
39795          * Fires when this tab is closed.
39796          * @param {Roo.TabPanelItem} this
39797          */
39798          "close": true,
39799         /**
39800          * @event deactivate
39801          * Fires when this tab is no longer the active tab.
39802          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39803          * @param {Roo.TabPanelItem} this
39804          */
39805          "deactivate" : true
39806     });
39807     this.hidden = false;
39808
39809     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39810 };
39811
39812 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39813            {
39814     purgeListeners : function(){
39815        Roo.util.Observable.prototype.purgeListeners.call(this);
39816        this.el.removeAllListeners();
39817     },
39818     /**
39819      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39820      */
39821     show : function(){
39822         this.status_node.addClass("active");
39823         this.showAction();
39824         if(Roo.isOpera){
39825             this.tabPanel.stripWrap.repaint();
39826         }
39827         this.fireEvent("activate", this.tabPanel, this);
39828     },
39829
39830     /**
39831      * Returns true if this tab is the active tab.
39832      * @return {Boolean}
39833      */
39834     isActive : function(){
39835         return this.tabPanel.getActiveTab() == this;
39836     },
39837
39838     /**
39839      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39840      */
39841     hide : function(){
39842         this.status_node.removeClass("active");
39843         this.hideAction();
39844         this.fireEvent("deactivate", this.tabPanel, this);
39845     },
39846
39847     hideAction : function(){
39848         this.bodyEl.hide();
39849         this.bodyEl.setStyle("position", "absolute");
39850         this.bodyEl.setLeft("-20000px");
39851         this.bodyEl.setTop("-20000px");
39852     },
39853
39854     showAction : function(){
39855         this.bodyEl.setStyle("position", "relative");
39856         this.bodyEl.setTop("");
39857         this.bodyEl.setLeft("");
39858         this.bodyEl.show();
39859     },
39860
39861     /**
39862      * Set the tooltip for the tab.
39863      * @param {String} tooltip The tab's tooltip
39864      */
39865     setTooltip : function(text){
39866         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39867             this.textEl.dom.qtip = text;
39868             this.textEl.dom.removeAttribute('title');
39869         }else{
39870             this.textEl.dom.title = text;
39871         }
39872     },
39873
39874     onTabClick : function(e){
39875         e.preventDefault();
39876         this.tabPanel.activate(this.id);
39877     },
39878
39879     onTabMouseDown : function(e){
39880         e.preventDefault();
39881         this.tabPanel.activate(this.id);
39882     },
39883 /*
39884     getWidth : function(){
39885         return this.inner.getWidth();
39886     },
39887
39888     setWidth : function(width){
39889         var iwidth = width - this.linode.getPadding("lr");
39890         this.inner.setWidth(iwidth);
39891         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39892         this.linode.setWidth(width);
39893     },
39894 */
39895     /**
39896      * Show or hide the tab
39897      * @param {Boolean} hidden True to hide or false to show.
39898      */
39899     setHidden : function(hidden){
39900         this.hidden = hidden;
39901         this.linode.setStyle("display", hidden ? "none" : "");
39902     },
39903
39904     /**
39905      * Returns true if this tab is "hidden"
39906      * @return {Boolean}
39907      */
39908     isHidden : function(){
39909         return this.hidden;
39910     },
39911
39912     /**
39913      * Returns the text for this tab
39914      * @return {String}
39915      */
39916     getText : function(){
39917         return this.text;
39918     },
39919     /*
39920     autoSize : function(){
39921         //this.el.beginMeasure();
39922         this.textEl.setWidth(1);
39923         /*
39924          *  #2804 [new] Tabs in Roojs
39925          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39926          */
39927         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39928         //this.el.endMeasure();
39929     //},
39930
39931     /**
39932      * Sets the text for the tab (Note: this also sets the tooltip text)
39933      * @param {String} text The tab's text and tooltip
39934      */
39935     setText : function(text){
39936         this.text = text;
39937         this.textEl.update(text);
39938         this.setTooltip(text);
39939         //if(!this.tabPanel.resizeTabs){
39940         //    this.autoSize();
39941         //}
39942     },
39943     /**
39944      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39945      */
39946     activate : function(){
39947         this.tabPanel.activate(this.id);
39948     },
39949
39950     /**
39951      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39952      */
39953     disable : function(){
39954         if(this.tabPanel.active != this){
39955             this.disabled = true;
39956             this.status_node.addClass("disabled");
39957         }
39958     },
39959
39960     /**
39961      * Enables this TabPanelItem if it was previously disabled.
39962      */
39963     enable : function(){
39964         this.disabled = false;
39965         this.status_node.removeClass("disabled");
39966     },
39967
39968     /**
39969      * Sets the content for this TabPanelItem.
39970      * @param {String} content The content
39971      * @param {Boolean} loadScripts true to look for and load scripts
39972      */
39973     setContent : function(content, loadScripts){
39974         this.bodyEl.update(content, loadScripts);
39975     },
39976
39977     /**
39978      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39979      * @return {Roo.UpdateManager} The UpdateManager
39980      */
39981     getUpdateManager : function(){
39982         return this.bodyEl.getUpdateManager();
39983     },
39984
39985     /**
39986      * Set a URL to be used to load the content for this TabPanelItem.
39987      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39988      * @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)
39989      * @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)
39990      * @return {Roo.UpdateManager} The UpdateManager
39991      */
39992     setUrl : function(url, params, loadOnce){
39993         if(this.refreshDelegate){
39994             this.un('activate', this.refreshDelegate);
39995         }
39996         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39997         this.on("activate", this.refreshDelegate);
39998         return this.bodyEl.getUpdateManager();
39999     },
40000
40001     /** @private */
40002     _handleRefresh : function(url, params, loadOnce){
40003         if(!loadOnce || !this.loaded){
40004             var updater = this.bodyEl.getUpdateManager();
40005             updater.update(url, params, this._setLoaded.createDelegate(this));
40006         }
40007     },
40008
40009     /**
40010      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40011      *   Will fail silently if the setUrl method has not been called.
40012      *   This does not activate the panel, just updates its content.
40013      */
40014     refresh : function(){
40015         if(this.refreshDelegate){
40016            this.loaded = false;
40017            this.refreshDelegate();
40018         }
40019     },
40020
40021     /** @private */
40022     _setLoaded : function(){
40023         this.loaded = true;
40024     },
40025
40026     /** @private */
40027     closeClick : function(e){
40028         var o = {};
40029         e.stopEvent();
40030         this.fireEvent("beforeclose", this, o);
40031         if(o.cancel !== true){
40032             this.tabPanel.removeTab(this.id);
40033         }
40034     },
40035     /**
40036      * The text displayed in the tooltip for the close icon.
40037      * @type String
40038      */
40039     closeText : "Close this tab"
40040 });
40041 /**
40042 *    This script refer to:
40043 *    Title: International Telephone Input
40044 *    Author: Jack O'Connor
40045 *    Code version:  v12.1.12
40046 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40047 **/
40048
40049 Roo.bootstrap.PhoneInputData = function() {
40050     var d = [
40051       [
40052         "Afghanistan (‫افغانستان‬‎)",
40053         "af",
40054         "93"
40055       ],
40056       [
40057         "Albania (Shqipëri)",
40058         "al",
40059         "355"
40060       ],
40061       [
40062         "Algeria (‫الجزائر‬‎)",
40063         "dz",
40064         "213"
40065       ],
40066       [
40067         "American Samoa",
40068         "as",
40069         "1684"
40070       ],
40071       [
40072         "Andorra",
40073         "ad",
40074         "376"
40075       ],
40076       [
40077         "Angola",
40078         "ao",
40079         "244"
40080       ],
40081       [
40082         "Anguilla",
40083         "ai",
40084         "1264"
40085       ],
40086       [
40087         "Antigua and Barbuda",
40088         "ag",
40089         "1268"
40090       ],
40091       [
40092         "Argentina",
40093         "ar",
40094         "54"
40095       ],
40096       [
40097         "Armenia (Հայաստան)",
40098         "am",
40099         "374"
40100       ],
40101       [
40102         "Aruba",
40103         "aw",
40104         "297"
40105       ],
40106       [
40107         "Australia",
40108         "au",
40109         "61",
40110         0
40111       ],
40112       [
40113         "Austria (Österreich)",
40114         "at",
40115         "43"
40116       ],
40117       [
40118         "Azerbaijan (Azərbaycan)",
40119         "az",
40120         "994"
40121       ],
40122       [
40123         "Bahamas",
40124         "bs",
40125         "1242"
40126       ],
40127       [
40128         "Bahrain (‫البحرين‬‎)",
40129         "bh",
40130         "973"
40131       ],
40132       [
40133         "Bangladesh (বাংলাদেশ)",
40134         "bd",
40135         "880"
40136       ],
40137       [
40138         "Barbados",
40139         "bb",
40140         "1246"
40141       ],
40142       [
40143         "Belarus (Беларусь)",
40144         "by",
40145         "375"
40146       ],
40147       [
40148         "Belgium (België)",
40149         "be",
40150         "32"
40151       ],
40152       [
40153         "Belize",
40154         "bz",
40155         "501"
40156       ],
40157       [
40158         "Benin (Bénin)",
40159         "bj",
40160         "229"
40161       ],
40162       [
40163         "Bermuda",
40164         "bm",
40165         "1441"
40166       ],
40167       [
40168         "Bhutan (འབྲུག)",
40169         "bt",
40170         "975"
40171       ],
40172       [
40173         "Bolivia",
40174         "bo",
40175         "591"
40176       ],
40177       [
40178         "Bosnia and Herzegovina (Босна и Херцеговина)",
40179         "ba",
40180         "387"
40181       ],
40182       [
40183         "Botswana",
40184         "bw",
40185         "267"
40186       ],
40187       [
40188         "Brazil (Brasil)",
40189         "br",
40190         "55"
40191       ],
40192       [
40193         "British Indian Ocean Territory",
40194         "io",
40195         "246"
40196       ],
40197       [
40198         "British Virgin Islands",
40199         "vg",
40200         "1284"
40201       ],
40202       [
40203         "Brunei",
40204         "bn",
40205         "673"
40206       ],
40207       [
40208         "Bulgaria (България)",
40209         "bg",
40210         "359"
40211       ],
40212       [
40213         "Burkina Faso",
40214         "bf",
40215         "226"
40216       ],
40217       [
40218         "Burundi (Uburundi)",
40219         "bi",
40220         "257"
40221       ],
40222       [
40223         "Cambodia (កម្ពុជា)",
40224         "kh",
40225         "855"
40226       ],
40227       [
40228         "Cameroon (Cameroun)",
40229         "cm",
40230         "237"
40231       ],
40232       [
40233         "Canada",
40234         "ca",
40235         "1",
40236         1,
40237         ["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"]
40238       ],
40239       [
40240         "Cape Verde (Kabu Verdi)",
40241         "cv",
40242         "238"
40243       ],
40244       [
40245         "Caribbean Netherlands",
40246         "bq",
40247         "599",
40248         1
40249       ],
40250       [
40251         "Cayman Islands",
40252         "ky",
40253         "1345"
40254       ],
40255       [
40256         "Central African Republic (République centrafricaine)",
40257         "cf",
40258         "236"
40259       ],
40260       [
40261         "Chad (Tchad)",
40262         "td",
40263         "235"
40264       ],
40265       [
40266         "Chile",
40267         "cl",
40268         "56"
40269       ],
40270       [
40271         "China (中国)",
40272         "cn",
40273         "86"
40274       ],
40275       [
40276         "Christmas Island",
40277         "cx",
40278         "61",
40279         2
40280       ],
40281       [
40282         "Cocos (Keeling) Islands",
40283         "cc",
40284         "61",
40285         1
40286       ],
40287       [
40288         "Colombia",
40289         "co",
40290         "57"
40291       ],
40292       [
40293         "Comoros (‫جزر القمر‬‎)",
40294         "km",
40295         "269"
40296       ],
40297       [
40298         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40299         "cd",
40300         "243"
40301       ],
40302       [
40303         "Congo (Republic) (Congo-Brazzaville)",
40304         "cg",
40305         "242"
40306       ],
40307       [
40308         "Cook Islands",
40309         "ck",
40310         "682"
40311       ],
40312       [
40313         "Costa Rica",
40314         "cr",
40315         "506"
40316       ],
40317       [
40318         "Côte d’Ivoire",
40319         "ci",
40320         "225"
40321       ],
40322       [
40323         "Croatia (Hrvatska)",
40324         "hr",
40325         "385"
40326       ],
40327       [
40328         "Cuba",
40329         "cu",
40330         "53"
40331       ],
40332       [
40333         "Curaçao",
40334         "cw",
40335         "599",
40336         0
40337       ],
40338       [
40339         "Cyprus (Κύπρος)",
40340         "cy",
40341         "357"
40342       ],
40343       [
40344         "Czech Republic (Česká republika)",
40345         "cz",
40346         "420"
40347       ],
40348       [
40349         "Denmark (Danmark)",
40350         "dk",
40351         "45"
40352       ],
40353       [
40354         "Djibouti",
40355         "dj",
40356         "253"
40357       ],
40358       [
40359         "Dominica",
40360         "dm",
40361         "1767"
40362       ],
40363       [
40364         "Dominican Republic (República Dominicana)",
40365         "do",
40366         "1",
40367         2,
40368         ["809", "829", "849"]
40369       ],
40370       [
40371         "Ecuador",
40372         "ec",
40373         "593"
40374       ],
40375       [
40376         "Egypt (‫مصر‬‎)",
40377         "eg",
40378         "20"
40379       ],
40380       [
40381         "El Salvador",
40382         "sv",
40383         "503"
40384       ],
40385       [
40386         "Equatorial Guinea (Guinea Ecuatorial)",
40387         "gq",
40388         "240"
40389       ],
40390       [
40391         "Eritrea",
40392         "er",
40393         "291"
40394       ],
40395       [
40396         "Estonia (Eesti)",
40397         "ee",
40398         "372"
40399       ],
40400       [
40401         "Ethiopia",
40402         "et",
40403         "251"
40404       ],
40405       [
40406         "Falkland Islands (Islas Malvinas)",
40407         "fk",
40408         "500"
40409       ],
40410       [
40411         "Faroe Islands (Føroyar)",
40412         "fo",
40413         "298"
40414       ],
40415       [
40416         "Fiji",
40417         "fj",
40418         "679"
40419       ],
40420       [
40421         "Finland (Suomi)",
40422         "fi",
40423         "358",
40424         0
40425       ],
40426       [
40427         "France",
40428         "fr",
40429         "33"
40430       ],
40431       [
40432         "French Guiana (Guyane française)",
40433         "gf",
40434         "594"
40435       ],
40436       [
40437         "French Polynesia (Polynésie française)",
40438         "pf",
40439         "689"
40440       ],
40441       [
40442         "Gabon",
40443         "ga",
40444         "241"
40445       ],
40446       [
40447         "Gambia",
40448         "gm",
40449         "220"
40450       ],
40451       [
40452         "Georgia (საქართველო)",
40453         "ge",
40454         "995"
40455       ],
40456       [
40457         "Germany (Deutschland)",
40458         "de",
40459         "49"
40460       ],
40461       [
40462         "Ghana (Gaana)",
40463         "gh",
40464         "233"
40465       ],
40466       [
40467         "Gibraltar",
40468         "gi",
40469         "350"
40470       ],
40471       [
40472         "Greece (Ελλάδα)",
40473         "gr",
40474         "30"
40475       ],
40476       [
40477         "Greenland (Kalaallit Nunaat)",
40478         "gl",
40479         "299"
40480       ],
40481       [
40482         "Grenada",
40483         "gd",
40484         "1473"
40485       ],
40486       [
40487         "Guadeloupe",
40488         "gp",
40489         "590",
40490         0
40491       ],
40492       [
40493         "Guam",
40494         "gu",
40495         "1671"
40496       ],
40497       [
40498         "Guatemala",
40499         "gt",
40500         "502"
40501       ],
40502       [
40503         "Guernsey",
40504         "gg",
40505         "44",
40506         1
40507       ],
40508       [
40509         "Guinea (Guinée)",
40510         "gn",
40511         "224"
40512       ],
40513       [
40514         "Guinea-Bissau (Guiné Bissau)",
40515         "gw",
40516         "245"
40517       ],
40518       [
40519         "Guyana",
40520         "gy",
40521         "592"
40522       ],
40523       [
40524         "Haiti",
40525         "ht",
40526         "509"
40527       ],
40528       [
40529         "Honduras",
40530         "hn",
40531         "504"
40532       ],
40533       [
40534         "Hong Kong (香港)",
40535         "hk",
40536         "852"
40537       ],
40538       [
40539         "Hungary (Magyarország)",
40540         "hu",
40541         "36"
40542       ],
40543       [
40544         "Iceland (Ísland)",
40545         "is",
40546         "354"
40547       ],
40548       [
40549         "India (भारत)",
40550         "in",
40551         "91"
40552       ],
40553       [
40554         "Indonesia",
40555         "id",
40556         "62"
40557       ],
40558       [
40559         "Iran (‫ایران‬‎)",
40560         "ir",
40561         "98"
40562       ],
40563       [
40564         "Iraq (‫العراق‬‎)",
40565         "iq",
40566         "964"
40567       ],
40568       [
40569         "Ireland",
40570         "ie",
40571         "353"
40572       ],
40573       [
40574         "Isle of Man",
40575         "im",
40576         "44",
40577         2
40578       ],
40579       [
40580         "Israel (‫ישראל‬‎)",
40581         "il",
40582         "972"
40583       ],
40584       [
40585         "Italy (Italia)",
40586         "it",
40587         "39",
40588         0
40589       ],
40590       [
40591         "Jamaica",
40592         "jm",
40593         "1876"
40594       ],
40595       [
40596         "Japan (日本)",
40597         "jp",
40598         "81"
40599       ],
40600       [
40601         "Jersey",
40602         "je",
40603         "44",
40604         3
40605       ],
40606       [
40607         "Jordan (‫الأردن‬‎)",
40608         "jo",
40609         "962"
40610       ],
40611       [
40612         "Kazakhstan (Казахстан)",
40613         "kz",
40614         "7",
40615         1
40616       ],
40617       [
40618         "Kenya",
40619         "ke",
40620         "254"
40621       ],
40622       [
40623         "Kiribati",
40624         "ki",
40625         "686"
40626       ],
40627       [
40628         "Kosovo",
40629         "xk",
40630         "383"
40631       ],
40632       [
40633         "Kuwait (‫الكويت‬‎)",
40634         "kw",
40635         "965"
40636       ],
40637       [
40638         "Kyrgyzstan (Кыргызстан)",
40639         "kg",
40640         "996"
40641       ],
40642       [
40643         "Laos (ລາວ)",
40644         "la",
40645         "856"
40646       ],
40647       [
40648         "Latvia (Latvija)",
40649         "lv",
40650         "371"
40651       ],
40652       [
40653         "Lebanon (‫لبنان‬‎)",
40654         "lb",
40655         "961"
40656       ],
40657       [
40658         "Lesotho",
40659         "ls",
40660         "266"
40661       ],
40662       [
40663         "Liberia",
40664         "lr",
40665         "231"
40666       ],
40667       [
40668         "Libya (‫ليبيا‬‎)",
40669         "ly",
40670         "218"
40671       ],
40672       [
40673         "Liechtenstein",
40674         "li",
40675         "423"
40676       ],
40677       [
40678         "Lithuania (Lietuva)",
40679         "lt",
40680         "370"
40681       ],
40682       [
40683         "Luxembourg",
40684         "lu",
40685         "352"
40686       ],
40687       [
40688         "Macau (澳門)",
40689         "mo",
40690         "853"
40691       ],
40692       [
40693         "Macedonia (FYROM) (Македонија)",
40694         "mk",
40695         "389"
40696       ],
40697       [
40698         "Madagascar (Madagasikara)",
40699         "mg",
40700         "261"
40701       ],
40702       [
40703         "Malawi",
40704         "mw",
40705         "265"
40706       ],
40707       [
40708         "Malaysia",
40709         "my",
40710         "60"
40711       ],
40712       [
40713         "Maldives",
40714         "mv",
40715         "960"
40716       ],
40717       [
40718         "Mali",
40719         "ml",
40720         "223"
40721       ],
40722       [
40723         "Malta",
40724         "mt",
40725         "356"
40726       ],
40727       [
40728         "Marshall Islands",
40729         "mh",
40730         "692"
40731       ],
40732       [
40733         "Martinique",
40734         "mq",
40735         "596"
40736       ],
40737       [
40738         "Mauritania (‫موريتانيا‬‎)",
40739         "mr",
40740         "222"
40741       ],
40742       [
40743         "Mauritius (Moris)",
40744         "mu",
40745         "230"
40746       ],
40747       [
40748         "Mayotte",
40749         "yt",
40750         "262",
40751         1
40752       ],
40753       [
40754         "Mexico (México)",
40755         "mx",
40756         "52"
40757       ],
40758       [
40759         "Micronesia",
40760         "fm",
40761         "691"
40762       ],
40763       [
40764         "Moldova (Republica Moldova)",
40765         "md",
40766         "373"
40767       ],
40768       [
40769         "Monaco",
40770         "mc",
40771         "377"
40772       ],
40773       [
40774         "Mongolia (Монгол)",
40775         "mn",
40776         "976"
40777       ],
40778       [
40779         "Montenegro (Crna Gora)",
40780         "me",
40781         "382"
40782       ],
40783       [
40784         "Montserrat",
40785         "ms",
40786         "1664"
40787       ],
40788       [
40789         "Morocco (‫المغرب‬‎)",
40790         "ma",
40791         "212",
40792         0
40793       ],
40794       [
40795         "Mozambique (Moçambique)",
40796         "mz",
40797         "258"
40798       ],
40799       [
40800         "Myanmar (Burma) (မြန်မာ)",
40801         "mm",
40802         "95"
40803       ],
40804       [
40805         "Namibia (Namibië)",
40806         "na",
40807         "264"
40808       ],
40809       [
40810         "Nauru",
40811         "nr",
40812         "674"
40813       ],
40814       [
40815         "Nepal (नेपाल)",
40816         "np",
40817         "977"
40818       ],
40819       [
40820         "Netherlands (Nederland)",
40821         "nl",
40822         "31"
40823       ],
40824       [
40825         "New Caledonia (Nouvelle-Calédonie)",
40826         "nc",
40827         "687"
40828       ],
40829       [
40830         "New Zealand",
40831         "nz",
40832         "64"
40833       ],
40834       [
40835         "Nicaragua",
40836         "ni",
40837         "505"
40838       ],
40839       [
40840         "Niger (Nijar)",
40841         "ne",
40842         "227"
40843       ],
40844       [
40845         "Nigeria",
40846         "ng",
40847         "234"
40848       ],
40849       [
40850         "Niue",
40851         "nu",
40852         "683"
40853       ],
40854       [
40855         "Norfolk Island",
40856         "nf",
40857         "672"
40858       ],
40859       [
40860         "North Korea (조선 민주주의 인민 공화국)",
40861         "kp",
40862         "850"
40863       ],
40864       [
40865         "Northern Mariana Islands",
40866         "mp",
40867         "1670"
40868       ],
40869       [
40870         "Norway (Norge)",
40871         "no",
40872         "47",
40873         0
40874       ],
40875       [
40876         "Oman (‫عُمان‬‎)",
40877         "om",
40878         "968"
40879       ],
40880       [
40881         "Pakistan (‫پاکستان‬‎)",
40882         "pk",
40883         "92"
40884       ],
40885       [
40886         "Palau",
40887         "pw",
40888         "680"
40889       ],
40890       [
40891         "Palestine (‫فلسطين‬‎)",
40892         "ps",
40893         "970"
40894       ],
40895       [
40896         "Panama (Panamá)",
40897         "pa",
40898         "507"
40899       ],
40900       [
40901         "Papua New Guinea",
40902         "pg",
40903         "675"
40904       ],
40905       [
40906         "Paraguay",
40907         "py",
40908         "595"
40909       ],
40910       [
40911         "Peru (Perú)",
40912         "pe",
40913         "51"
40914       ],
40915       [
40916         "Philippines",
40917         "ph",
40918         "63"
40919       ],
40920       [
40921         "Poland (Polska)",
40922         "pl",
40923         "48"
40924       ],
40925       [
40926         "Portugal",
40927         "pt",
40928         "351"
40929       ],
40930       [
40931         "Puerto Rico",
40932         "pr",
40933         "1",
40934         3,
40935         ["787", "939"]
40936       ],
40937       [
40938         "Qatar (‫قطر‬‎)",
40939         "qa",
40940         "974"
40941       ],
40942       [
40943         "Réunion (La Réunion)",
40944         "re",
40945         "262",
40946         0
40947       ],
40948       [
40949         "Romania (România)",
40950         "ro",
40951         "40"
40952       ],
40953       [
40954         "Russia (Россия)",
40955         "ru",
40956         "7",
40957         0
40958       ],
40959       [
40960         "Rwanda",
40961         "rw",
40962         "250"
40963       ],
40964       [
40965         "Saint Barthélemy",
40966         "bl",
40967         "590",
40968         1
40969       ],
40970       [
40971         "Saint Helena",
40972         "sh",
40973         "290"
40974       ],
40975       [
40976         "Saint Kitts and Nevis",
40977         "kn",
40978         "1869"
40979       ],
40980       [
40981         "Saint Lucia",
40982         "lc",
40983         "1758"
40984       ],
40985       [
40986         "Saint Martin (Saint-Martin (partie française))",
40987         "mf",
40988         "590",
40989         2
40990       ],
40991       [
40992         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40993         "pm",
40994         "508"
40995       ],
40996       [
40997         "Saint Vincent and the Grenadines",
40998         "vc",
40999         "1784"
41000       ],
41001       [
41002         "Samoa",
41003         "ws",
41004         "685"
41005       ],
41006       [
41007         "San Marino",
41008         "sm",
41009         "378"
41010       ],
41011       [
41012         "São Tomé and Príncipe (São Tomé e Príncipe)",
41013         "st",
41014         "239"
41015       ],
41016       [
41017         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41018         "sa",
41019         "966"
41020       ],
41021       [
41022         "Senegal (Sénégal)",
41023         "sn",
41024         "221"
41025       ],
41026       [
41027         "Serbia (Србија)",
41028         "rs",
41029         "381"
41030       ],
41031       [
41032         "Seychelles",
41033         "sc",
41034         "248"
41035       ],
41036       [
41037         "Sierra Leone",
41038         "sl",
41039         "232"
41040       ],
41041       [
41042         "Singapore",
41043         "sg",
41044         "65"
41045       ],
41046       [
41047         "Sint Maarten",
41048         "sx",
41049         "1721"
41050       ],
41051       [
41052         "Slovakia (Slovensko)",
41053         "sk",
41054         "421"
41055       ],
41056       [
41057         "Slovenia (Slovenija)",
41058         "si",
41059         "386"
41060       ],
41061       [
41062         "Solomon Islands",
41063         "sb",
41064         "677"
41065       ],
41066       [
41067         "Somalia (Soomaaliya)",
41068         "so",
41069         "252"
41070       ],
41071       [
41072         "South Africa",
41073         "za",
41074         "27"
41075       ],
41076       [
41077         "South Korea (대한민국)",
41078         "kr",
41079         "82"
41080       ],
41081       [
41082         "South Sudan (‫جنوب السودان‬‎)",
41083         "ss",
41084         "211"
41085       ],
41086       [
41087         "Spain (España)",
41088         "es",
41089         "34"
41090       ],
41091       [
41092         "Sri Lanka (ශ්‍රී ලංකාව)",
41093         "lk",
41094         "94"
41095       ],
41096       [
41097         "Sudan (‫السودان‬‎)",
41098         "sd",
41099         "249"
41100       ],
41101       [
41102         "Suriname",
41103         "sr",
41104         "597"
41105       ],
41106       [
41107         "Svalbard and Jan Mayen",
41108         "sj",
41109         "47",
41110         1
41111       ],
41112       [
41113         "Swaziland",
41114         "sz",
41115         "268"
41116       ],
41117       [
41118         "Sweden (Sverige)",
41119         "se",
41120         "46"
41121       ],
41122       [
41123         "Switzerland (Schweiz)",
41124         "ch",
41125         "41"
41126       ],
41127       [
41128         "Syria (‫سوريا‬‎)",
41129         "sy",
41130         "963"
41131       ],
41132       [
41133         "Taiwan (台灣)",
41134         "tw",
41135         "886"
41136       ],
41137       [
41138         "Tajikistan",
41139         "tj",
41140         "992"
41141       ],
41142       [
41143         "Tanzania",
41144         "tz",
41145         "255"
41146       ],
41147       [
41148         "Thailand (ไทย)",
41149         "th",
41150         "66"
41151       ],
41152       [
41153         "Timor-Leste",
41154         "tl",
41155         "670"
41156       ],
41157       [
41158         "Togo",
41159         "tg",
41160         "228"
41161       ],
41162       [
41163         "Tokelau",
41164         "tk",
41165         "690"
41166       ],
41167       [
41168         "Tonga",
41169         "to",
41170         "676"
41171       ],
41172       [
41173         "Trinidad and Tobago",
41174         "tt",
41175         "1868"
41176       ],
41177       [
41178         "Tunisia (‫تونس‬‎)",
41179         "tn",
41180         "216"
41181       ],
41182       [
41183         "Turkey (Türkiye)",
41184         "tr",
41185         "90"
41186       ],
41187       [
41188         "Turkmenistan",
41189         "tm",
41190         "993"
41191       ],
41192       [
41193         "Turks and Caicos Islands",
41194         "tc",
41195         "1649"
41196       ],
41197       [
41198         "Tuvalu",
41199         "tv",
41200         "688"
41201       ],
41202       [
41203         "U.S. Virgin Islands",
41204         "vi",
41205         "1340"
41206       ],
41207       [
41208         "Uganda",
41209         "ug",
41210         "256"
41211       ],
41212       [
41213         "Ukraine (Україна)",
41214         "ua",
41215         "380"
41216       ],
41217       [
41218         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41219         "ae",
41220         "971"
41221       ],
41222       [
41223         "United Kingdom",
41224         "gb",
41225         "44",
41226         0
41227       ],
41228       [
41229         "United States",
41230         "us",
41231         "1",
41232         0
41233       ],
41234       [
41235         "Uruguay",
41236         "uy",
41237         "598"
41238       ],
41239       [
41240         "Uzbekistan (Oʻzbekiston)",
41241         "uz",
41242         "998"
41243       ],
41244       [
41245         "Vanuatu",
41246         "vu",
41247         "678"
41248       ],
41249       [
41250         "Vatican City (Città del Vaticano)",
41251         "va",
41252         "39",
41253         1
41254       ],
41255       [
41256         "Venezuela",
41257         "ve",
41258         "58"
41259       ],
41260       [
41261         "Vietnam (Việt Nam)",
41262         "vn",
41263         "84"
41264       ],
41265       [
41266         "Wallis and Futuna (Wallis-et-Futuna)",
41267         "wf",
41268         "681"
41269       ],
41270       [
41271         "Western Sahara (‫الصحراء الغربية‬‎)",
41272         "eh",
41273         "212",
41274         1
41275       ],
41276       [
41277         "Yemen (‫اليمن‬‎)",
41278         "ye",
41279         "967"
41280       ],
41281       [
41282         "Zambia",
41283         "zm",
41284         "260"
41285       ],
41286       [
41287         "Zimbabwe",
41288         "zw",
41289         "263"
41290       ],
41291       [
41292         "Åland Islands",
41293         "ax",
41294         "358",
41295         1
41296       ]
41297   ];
41298   
41299   return d;
41300 }/**
41301 *    This script refer to:
41302 *    Title: International Telephone Input
41303 *    Author: Jack O'Connor
41304 *    Code version:  v12.1.12
41305 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41306 **/
41307
41308 /**
41309  * @class Roo.bootstrap.PhoneInput
41310  * @extends Roo.bootstrap.TriggerField
41311  * An input with International dial-code selection
41312  
41313  * @cfg {String} defaultDialCode default '+852'
41314  * @cfg {Array} preferedCountries default []
41315   
41316  * @constructor
41317  * Create a new PhoneInput.
41318  * @param {Object} config Configuration options
41319  */
41320
41321 Roo.bootstrap.PhoneInput = function(config) {
41322     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41323 };
41324
41325 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41326         
41327         listWidth: undefined,
41328         
41329         selectedClass: 'active',
41330         
41331         invalidClass : "has-warning",
41332         
41333         validClass: 'has-success',
41334         
41335         allowed: '0123456789',
41336         
41337         max_length: 15,
41338         
41339         /**
41340          * @cfg {String} defaultDialCode The default dial code when initializing the input
41341          */
41342         defaultDialCode: '+852',
41343         
41344         /**
41345          * @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
41346          */
41347         preferedCountries: false,
41348         
41349         getAutoCreate : function()
41350         {
41351             var data = Roo.bootstrap.PhoneInputData();
41352             var align = this.labelAlign || this.parentLabelAlign();
41353             var id = Roo.id();
41354             
41355             this.allCountries = [];
41356             this.dialCodeMapping = [];
41357             
41358             for (var i = 0; i < data.length; i++) {
41359               var c = data[i];
41360               this.allCountries[i] = {
41361                 name: c[0],
41362                 iso2: c[1],
41363                 dialCode: c[2],
41364                 priority: c[3] || 0,
41365                 areaCodes: c[4] || null
41366               };
41367               this.dialCodeMapping[c[2]] = {
41368                   name: c[0],
41369                   iso2: c[1],
41370                   priority: c[3] || 0,
41371                   areaCodes: c[4] || null
41372               };
41373             }
41374             
41375             var cfg = {
41376                 cls: 'form-group',
41377                 cn: []
41378             };
41379             
41380             var input =  {
41381                 tag: 'input',
41382                 id : id,
41383                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41384                 maxlength: this.max_length,
41385                 cls : 'form-control tel-input',
41386                 autocomplete: 'new-password'
41387             };
41388             
41389             var hiddenInput = {
41390                 tag: 'input',
41391                 type: 'hidden',
41392                 cls: 'hidden-tel-input'
41393             };
41394             
41395             if (this.name) {
41396                 hiddenInput.name = this.name;
41397             }
41398             
41399             if (this.disabled) {
41400                 input.disabled = true;
41401             }
41402             
41403             var flag_container = {
41404                 tag: 'div',
41405                 cls: 'flag-box',
41406                 cn: [
41407                     {
41408                         tag: 'div',
41409                         cls: 'flag'
41410                     },
41411                     {
41412                         tag: 'div',
41413                         cls: 'caret'
41414                     }
41415                 ]
41416             };
41417             
41418             var box = {
41419                 tag: 'div',
41420                 cls: this.hasFeedback ? 'has-feedback' : '',
41421                 cn: [
41422                     hiddenInput,
41423                     input,
41424                     {
41425                         tag: 'input',
41426                         cls: 'dial-code-holder',
41427                         disabled: true
41428                     }
41429                 ]
41430             };
41431             
41432             var container = {
41433                 cls: 'roo-select2-container input-group',
41434                 cn: [
41435                     flag_container,
41436                     box
41437                 ]
41438             };
41439             
41440             if (this.fieldLabel.length) {
41441                 var indicator = {
41442                     tag: 'i',
41443                     tooltip: 'This field is required'
41444                 };
41445                 
41446                 var label = {
41447                     tag: 'label',
41448                     'for':  id,
41449                     cls: 'control-label',
41450                     cn: []
41451                 };
41452                 
41453                 var label_text = {
41454                     tag: 'span',
41455                     html: this.fieldLabel
41456                 };
41457                 
41458                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41459                 label.cn = [
41460                     indicator,
41461                     label_text
41462                 ];
41463                 
41464                 if(this.indicatorpos == 'right') {
41465                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41466                     label.cn = [
41467                         label_text,
41468                         indicator
41469                     ];
41470                 }
41471                 
41472                 if(align == 'left') {
41473                     container = {
41474                         tag: 'div',
41475                         cn: [
41476                             container
41477                         ]
41478                     };
41479                     
41480                     if(this.labelWidth > 12){
41481                         label.style = "width: " + this.labelWidth + 'px';
41482                     }
41483                     if(this.labelWidth < 13 && this.labelmd == 0){
41484                         this.labelmd = this.labelWidth;
41485                     }
41486                     if(this.labellg > 0){
41487                         label.cls += ' col-lg-' + this.labellg;
41488                         input.cls += ' col-lg-' + (12 - this.labellg);
41489                     }
41490                     if(this.labelmd > 0){
41491                         label.cls += ' col-md-' + this.labelmd;
41492                         container.cls += ' col-md-' + (12 - this.labelmd);
41493                     }
41494                     if(this.labelsm > 0){
41495                         label.cls += ' col-sm-' + this.labelsm;
41496                         container.cls += ' col-sm-' + (12 - this.labelsm);
41497                     }
41498                     if(this.labelxs > 0){
41499                         label.cls += ' col-xs-' + this.labelxs;
41500                         container.cls += ' col-xs-' + (12 - this.labelxs);
41501                     }
41502                 }
41503             }
41504             
41505             cfg.cn = [
41506                 label,
41507                 container
41508             ];
41509             
41510             var settings = this;
41511             
41512             ['xs','sm','md','lg'].map(function(size){
41513                 if (settings[size]) {
41514                     cfg.cls += ' col-' + size + '-' + settings[size];
41515                 }
41516             });
41517             
41518             this.store = new Roo.data.Store({
41519                 proxy : new Roo.data.MemoryProxy({}),
41520                 reader : new Roo.data.JsonReader({
41521                     fields : [
41522                         {
41523                             'name' : 'name',
41524                             'type' : 'string'
41525                         },
41526                         {
41527                             'name' : 'iso2',
41528                             'type' : 'string'
41529                         },
41530                         {
41531                             'name' : 'dialCode',
41532                             'type' : 'string'
41533                         },
41534                         {
41535                             'name' : 'priority',
41536                             'type' : 'string'
41537                         },
41538                         {
41539                             'name' : 'areaCodes',
41540                             'type' : 'string'
41541                         }
41542                     ]
41543                 })
41544             });
41545             
41546             if(!this.preferedCountries) {
41547                 this.preferedCountries = [
41548                     'hk',
41549                     'gb',
41550                     'us'
41551                 ];
41552             }
41553             
41554             var p = this.preferedCountries.reverse();
41555             
41556             if(p) {
41557                 for (var i = 0; i < p.length; i++) {
41558                     for (var j = 0; j < this.allCountries.length; j++) {
41559                         if(this.allCountries[j].iso2 == p[i]) {
41560                             var t = this.allCountries[j];
41561                             this.allCountries.splice(j,1);
41562                             this.allCountries.unshift(t);
41563                         }
41564                     } 
41565                 }
41566             }
41567             
41568             this.store.proxy.data = {
41569                 success: true,
41570                 data: this.allCountries
41571             };
41572             
41573             return cfg;
41574         },
41575         
41576         initEvents : function()
41577         {
41578             this.createList();
41579             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41580             
41581             this.indicator = this.indicatorEl();
41582             this.flag = this.flagEl();
41583             this.dialCodeHolder = this.dialCodeHolderEl();
41584             
41585             this.trigger = this.el.select('div.flag-box',true).first();
41586             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41587             
41588             var _this = this;
41589             
41590             (function(){
41591                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41592                 _this.list.setWidth(lw);
41593             }).defer(100);
41594             
41595             this.list.on('mouseover', this.onViewOver, this);
41596             this.list.on('mousemove', this.onViewMove, this);
41597             this.inputEl().on("keyup", this.onKeyUp, this);
41598             this.inputEl().on("keypress", this.onKeyPress, this);
41599             
41600             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41601
41602             this.view = new Roo.View(this.list, this.tpl, {
41603                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41604             });
41605             
41606             this.view.on('click', this.onViewClick, this);
41607             this.setValue(this.defaultDialCode);
41608         },
41609         
41610         onTriggerClick : function(e)
41611         {
41612             Roo.log('trigger click');
41613             if(this.disabled){
41614                 return;
41615             }
41616             
41617             if(this.isExpanded()){
41618                 this.collapse();
41619                 this.hasFocus = false;
41620             }else {
41621                 this.store.load({});
41622                 this.hasFocus = true;
41623                 this.expand();
41624             }
41625         },
41626         
41627         isExpanded : function()
41628         {
41629             return this.list.isVisible();
41630         },
41631         
41632         collapse : function()
41633         {
41634             if(!this.isExpanded()){
41635                 return;
41636             }
41637             this.list.hide();
41638             Roo.get(document).un('mousedown', this.collapseIf, this);
41639             Roo.get(document).un('mousewheel', this.collapseIf, this);
41640             this.fireEvent('collapse', this);
41641             this.validate();
41642         },
41643         
41644         expand : function()
41645         {
41646             Roo.log('expand');
41647
41648             if(this.isExpanded() || !this.hasFocus){
41649                 return;
41650             }
41651             
41652             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41653             this.list.setWidth(lw);
41654             
41655             this.list.show();
41656             this.restrictHeight();
41657             
41658             Roo.get(document).on('mousedown', this.collapseIf, this);
41659             Roo.get(document).on('mousewheel', this.collapseIf, this);
41660             
41661             this.fireEvent('expand', this);
41662         },
41663         
41664         restrictHeight : function()
41665         {
41666             this.list.alignTo(this.inputEl(), this.listAlign);
41667             this.list.alignTo(this.inputEl(), this.listAlign);
41668         },
41669         
41670         onViewOver : function(e, t)
41671         {
41672             if(this.inKeyMode){
41673                 return;
41674             }
41675             var item = this.view.findItemFromChild(t);
41676             
41677             if(item){
41678                 var index = this.view.indexOf(item);
41679                 this.select(index, false);
41680             }
41681         },
41682
41683         // private
41684         onViewClick : function(view, doFocus, el, e)
41685         {
41686             var index = this.view.getSelectedIndexes()[0];
41687             
41688             var r = this.store.getAt(index);
41689             
41690             if(r){
41691                 this.onSelect(r, index);
41692             }
41693             if(doFocus !== false && !this.blockFocus){
41694                 this.inputEl().focus();
41695             }
41696         },
41697         
41698         onViewMove : function(e, t)
41699         {
41700             this.inKeyMode = false;
41701         },
41702         
41703         select : function(index, scrollIntoView)
41704         {
41705             this.selectedIndex = index;
41706             this.view.select(index);
41707             if(scrollIntoView !== false){
41708                 var el = this.view.getNode(index);
41709                 if(el){
41710                     this.list.scrollChildIntoView(el, false);
41711                 }
41712             }
41713         },
41714         
41715         createList : function()
41716         {
41717             this.list = Roo.get(document.body).createChild({
41718                 tag: 'ul',
41719                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41720                 style: 'display:none'
41721             });
41722             
41723             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41724         },
41725         
41726         collapseIf : function(e)
41727         {
41728             var in_combo  = e.within(this.el);
41729             var in_list =  e.within(this.list);
41730             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41731             
41732             if (in_combo || in_list || is_list) {
41733                 return;
41734             }
41735             this.collapse();
41736         },
41737         
41738         onSelect : function(record, index)
41739         {
41740             if(this.fireEvent('beforeselect', this, record, index) !== false){
41741                 
41742                 this.setFlagClass(record.data.iso2);
41743                 this.setDialCode(record.data.dialCode);
41744                 this.hasFocus = false;
41745                 this.collapse();
41746                 this.fireEvent('select', this, record, index);
41747             }
41748         },
41749         
41750         flagEl : function()
41751         {
41752             var flag = this.el.select('div.flag',true).first();
41753             if(!flag){
41754                 return false;
41755             }
41756             return flag;
41757         },
41758         
41759         dialCodeHolderEl : function()
41760         {
41761             var d = this.el.select('input.dial-code-holder',true).first();
41762             if(!d){
41763                 return false;
41764             }
41765             return d;
41766         },
41767         
41768         setDialCode : function(v)
41769         {
41770             this.dialCodeHolder.dom.value = '+'+v;
41771         },
41772         
41773         setFlagClass : function(n)
41774         {
41775             this.flag.dom.className = 'flag '+n;
41776         },
41777         
41778         getValue : function()
41779         {
41780             var v = this.inputEl().getValue();
41781             if(this.dialCodeHolder) {
41782                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41783             }
41784             return v;
41785         },
41786         
41787         setValue : function(v)
41788         {
41789             var d = this.getDialCode(v);
41790             
41791             //invalid dial code
41792             if(v.length == 0 || !d || d.length == 0) {
41793                 if(this.rendered){
41794                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41795                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41796                 }
41797                 return;
41798             }
41799             
41800             //valid dial code
41801             this.setFlagClass(this.dialCodeMapping[d].iso2);
41802             this.setDialCode(d);
41803             this.inputEl().dom.value = v.replace('+'+d,'');
41804             this.hiddenEl().dom.value = this.getValue();
41805             
41806             this.validate();
41807         },
41808         
41809         getDialCode : function(v)
41810         {
41811             v = v ||  '';
41812             
41813             if (v.length == 0) {
41814                 return this.dialCodeHolder.dom.value;
41815             }
41816             
41817             var dialCode = "";
41818             if (v.charAt(0) != "+") {
41819                 return false;
41820             }
41821             var numericChars = "";
41822             for (var i = 1; i < v.length; i++) {
41823               var c = v.charAt(i);
41824               if (!isNaN(c)) {
41825                 numericChars += c;
41826                 if (this.dialCodeMapping[numericChars]) {
41827                   dialCode = v.substr(1, i);
41828                 }
41829                 if (numericChars.length == 4) {
41830                   break;
41831                 }
41832               }
41833             }
41834             return dialCode;
41835         },
41836         
41837         reset : function()
41838         {
41839             this.setValue(this.defaultDialCode);
41840             this.validate();
41841         },
41842         
41843         hiddenEl : function()
41844         {
41845             return this.el.select('input.hidden-tel-input',true).first();
41846         },
41847         
41848         // after setting val
41849         onKeyUp : function(e){
41850             this.setValue(this.getValue());
41851         },
41852         
41853         onKeyPress : function(e){
41854             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41855                 e.stopEvent();
41856             }
41857         }
41858         
41859 });
41860 /**
41861  * @class Roo.bootstrap.MoneyField
41862  * @extends Roo.bootstrap.ComboBox
41863  * Bootstrap MoneyField class
41864  * 
41865  * @constructor
41866  * Create a new MoneyField.
41867  * @param {Object} config Configuration options
41868  */
41869
41870 Roo.bootstrap.MoneyField = function(config) {
41871     
41872     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41873     
41874 };
41875
41876 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41877     
41878     /**
41879      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41880      */
41881     allowDecimals : true,
41882     /**
41883      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41884      */
41885     decimalSeparator : ".",
41886     /**
41887      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41888      */
41889     decimalPrecision : 0,
41890     /**
41891      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41892      */
41893     allowNegative : true,
41894     /**
41895      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41896      */
41897     allowZero: true,
41898     /**
41899      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41900      */
41901     minValue : Number.NEGATIVE_INFINITY,
41902     /**
41903      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41904      */
41905     maxValue : Number.MAX_VALUE,
41906     /**
41907      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41908      */
41909     minText : "The minimum value for this field is {0}",
41910     /**
41911      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41912      */
41913     maxText : "The maximum value for this field is {0}",
41914     /**
41915      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41916      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41917      */
41918     nanText : "{0} is not a valid number",
41919     /**
41920      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41921      */
41922     castInt : true,
41923     /**
41924      * @cfg {String} defaults currency of the MoneyField
41925      * value should be in lkey
41926      */
41927     defaultCurrency : false,
41928     /**
41929      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41930      */
41931     thousandsDelimiter : false,
41932     /**
41933      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41934      */
41935     max_length: false,
41936     
41937     inputlg : 9,
41938     inputmd : 9,
41939     inputsm : 9,
41940     inputxs : 6,
41941     
41942     store : false,
41943     
41944     getAutoCreate : function()
41945     {
41946         var align = this.labelAlign || this.parentLabelAlign();
41947         
41948         var id = Roo.id();
41949
41950         var cfg = {
41951             cls: 'form-group',
41952             cn: []
41953         };
41954
41955         var input =  {
41956             tag: 'input',
41957             id : id,
41958             cls : 'form-control roo-money-amount-input',
41959             autocomplete: 'new-password'
41960         };
41961         
41962         var hiddenInput = {
41963             tag: 'input',
41964             type: 'hidden',
41965             id: Roo.id(),
41966             cls: 'hidden-number-input'
41967         };
41968         
41969         if(this.max_length) {
41970             input.maxlength = this.max_length; 
41971         }
41972         
41973         if (this.name) {
41974             hiddenInput.name = this.name;
41975         }
41976
41977         if (this.disabled) {
41978             input.disabled = true;
41979         }
41980
41981         var clg = 12 - this.inputlg;
41982         var cmd = 12 - this.inputmd;
41983         var csm = 12 - this.inputsm;
41984         var cxs = 12 - this.inputxs;
41985         
41986         var container = {
41987             tag : 'div',
41988             cls : 'row roo-money-field',
41989             cn : [
41990                 {
41991                     tag : 'div',
41992                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41993                     cn : [
41994                         {
41995                             tag : 'div',
41996                             cls: 'roo-select2-container input-group',
41997                             cn: [
41998                                 {
41999                                     tag : 'input',
42000                                     cls : 'form-control roo-money-currency-input',
42001                                     autocomplete: 'new-password',
42002                                     readOnly : 1,
42003                                     name : this.currencyName
42004                                 },
42005                                 {
42006                                     tag :'span',
42007                                     cls : 'input-group-addon',
42008                                     cn : [
42009                                         {
42010                                             tag: 'span',
42011                                             cls: 'caret'
42012                                         }
42013                                     ]
42014                                 }
42015                             ]
42016                         }
42017                     ]
42018                 },
42019                 {
42020                     tag : 'div',
42021                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42022                     cn : [
42023                         {
42024                             tag: 'div',
42025                             cls: this.hasFeedback ? 'has-feedback' : '',
42026                             cn: [
42027                                 input
42028                             ]
42029                         }
42030                     ]
42031                 }
42032             ]
42033             
42034         };
42035         
42036         if (this.fieldLabel.length) {
42037             var indicator = {
42038                 tag: 'i',
42039                 tooltip: 'This field is required'
42040             };
42041
42042             var label = {
42043                 tag: 'label',
42044                 'for':  id,
42045                 cls: 'control-label',
42046                 cn: []
42047             };
42048
42049             var label_text = {
42050                 tag: 'span',
42051                 html: this.fieldLabel
42052             };
42053
42054             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42055             label.cn = [
42056                 indicator,
42057                 label_text
42058             ];
42059
42060             if(this.indicatorpos == 'right') {
42061                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42062                 label.cn = [
42063                     label_text,
42064                     indicator
42065                 ];
42066             }
42067
42068             if(align == 'left') {
42069                 container = {
42070                     tag: 'div',
42071                     cn: [
42072                         container
42073                     ]
42074                 };
42075
42076                 if(this.labelWidth > 12){
42077                     label.style = "width: " + this.labelWidth + 'px';
42078                 }
42079                 if(this.labelWidth < 13 && this.labelmd == 0){
42080                     this.labelmd = this.labelWidth;
42081                 }
42082                 if(this.labellg > 0){
42083                     label.cls += ' col-lg-' + this.labellg;
42084                     input.cls += ' col-lg-' + (12 - this.labellg);
42085                 }
42086                 if(this.labelmd > 0){
42087                     label.cls += ' col-md-' + this.labelmd;
42088                     container.cls += ' col-md-' + (12 - this.labelmd);
42089                 }
42090                 if(this.labelsm > 0){
42091                     label.cls += ' col-sm-' + this.labelsm;
42092                     container.cls += ' col-sm-' + (12 - this.labelsm);
42093                 }
42094                 if(this.labelxs > 0){
42095                     label.cls += ' col-xs-' + this.labelxs;
42096                     container.cls += ' col-xs-' + (12 - this.labelxs);
42097                 }
42098             }
42099         }
42100
42101         cfg.cn = [
42102             label,
42103             container,
42104             hiddenInput
42105         ];
42106         
42107         var settings = this;
42108
42109         ['xs','sm','md','lg'].map(function(size){
42110             if (settings[size]) {
42111                 cfg.cls += ' col-' + size + '-' + settings[size];
42112             }
42113         });
42114         
42115         return cfg;
42116     },
42117     
42118     initEvents : function()
42119     {
42120         this.indicator = this.indicatorEl();
42121         
42122         this.initCurrencyEvent();
42123         
42124         this.initNumberEvent();
42125     },
42126     
42127     initCurrencyEvent : function()
42128     {
42129         if (!this.store) {
42130             throw "can not find store for combo";
42131         }
42132         
42133         this.store = Roo.factory(this.store, Roo.data);
42134         this.store.parent = this;
42135         
42136         this.createList();
42137         
42138         this.triggerEl = this.el.select('.input-group-addon', true).first();
42139         
42140         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42141         
42142         var _this = this;
42143         
42144         (function(){
42145             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42146             _this.list.setWidth(lw);
42147         }).defer(100);
42148         
42149         this.list.on('mouseover', this.onViewOver, this);
42150         this.list.on('mousemove', this.onViewMove, this);
42151         this.list.on('scroll', this.onViewScroll, this);
42152         
42153         if(!this.tpl){
42154             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42155         }
42156         
42157         this.view = new Roo.View(this.list, this.tpl, {
42158             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42159         });
42160         
42161         this.view.on('click', this.onViewClick, this);
42162         
42163         this.store.on('beforeload', this.onBeforeLoad, this);
42164         this.store.on('load', this.onLoad, this);
42165         this.store.on('loadexception', this.onLoadException, this);
42166         
42167         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42168             "up" : function(e){
42169                 this.inKeyMode = true;
42170                 this.selectPrev();
42171             },
42172
42173             "down" : function(e){
42174                 if(!this.isExpanded()){
42175                     this.onTriggerClick();
42176                 }else{
42177                     this.inKeyMode = true;
42178                     this.selectNext();
42179                 }
42180             },
42181
42182             "enter" : function(e){
42183                 this.collapse();
42184                 
42185                 if(this.fireEvent("specialkey", this, e)){
42186                     this.onViewClick(false);
42187                 }
42188                 
42189                 return true;
42190             },
42191
42192             "esc" : function(e){
42193                 this.collapse();
42194             },
42195
42196             "tab" : function(e){
42197                 this.collapse();
42198                 
42199                 if(this.fireEvent("specialkey", this, e)){
42200                     this.onViewClick(false);
42201                 }
42202                 
42203                 return true;
42204             },
42205
42206             scope : this,
42207
42208             doRelay : function(foo, bar, hname){
42209                 if(hname == 'down' || this.scope.isExpanded()){
42210                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42211                 }
42212                 return true;
42213             },
42214
42215             forceKeyDown: true
42216         });
42217         
42218         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42219         
42220     },
42221     
42222     initNumberEvent : function(e)
42223     {
42224         this.inputEl().on("keydown" , this.fireKey,  this);
42225         this.inputEl().on("focus", this.onFocus,  this);
42226         this.inputEl().on("blur", this.onBlur,  this);
42227         
42228         this.inputEl().relayEvent('keyup', this);
42229         
42230         if(this.indicator){
42231             this.indicator.addClass('invisible');
42232         }
42233  
42234         this.originalValue = this.getValue();
42235         
42236         if(this.validationEvent == 'keyup'){
42237             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42238             this.inputEl().on('keyup', this.filterValidation, this);
42239         }
42240         else if(this.validationEvent !== false){
42241             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42242         }
42243         
42244         if(this.selectOnFocus){
42245             this.on("focus", this.preFocus, this);
42246             
42247         }
42248         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42249             this.inputEl().on("keypress", this.filterKeys, this);
42250         } else {
42251             this.inputEl().relayEvent('keypress', this);
42252         }
42253         
42254         var allowed = "0123456789";
42255         
42256         if(this.allowDecimals){
42257             allowed += this.decimalSeparator;
42258         }
42259         
42260         if(this.allowNegative){
42261             allowed += "-";
42262         }
42263         
42264         if(this.thousandsDelimiter) {
42265             allowed += ",";
42266         }
42267         
42268         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42269         
42270         var keyPress = function(e){
42271             
42272             var k = e.getKey();
42273             
42274             var c = e.getCharCode();
42275             
42276             if(
42277                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42278                     allowed.indexOf(String.fromCharCode(c)) === -1
42279             ){
42280                 e.stopEvent();
42281                 return;
42282             }
42283             
42284             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42285                 return;
42286             }
42287             
42288             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42289                 e.stopEvent();
42290             }
42291         };
42292         
42293         this.inputEl().on("keypress", keyPress, this);
42294         
42295     },
42296     
42297     onTriggerClick : function(e)
42298     {   
42299         if(this.disabled){
42300             return;
42301         }
42302         
42303         this.page = 0;
42304         this.loadNext = false;
42305         
42306         if(this.isExpanded()){
42307             this.collapse();
42308             return;
42309         }
42310         
42311         this.hasFocus = true;
42312         
42313         if(this.triggerAction == 'all') {
42314             this.doQuery(this.allQuery, true);
42315             return;
42316         }
42317         
42318         this.doQuery(this.getRawValue());
42319     },
42320     
42321     getCurrency : function()
42322     {   
42323         var v = this.currencyEl().getValue();
42324         
42325         return v;
42326     },
42327     
42328     restrictHeight : function()
42329     {
42330         this.list.alignTo(this.currencyEl(), this.listAlign);
42331         this.list.alignTo(this.currencyEl(), this.listAlign);
42332     },
42333     
42334     onViewClick : function(view, doFocus, el, e)
42335     {
42336         var index = this.view.getSelectedIndexes()[0];
42337         
42338         var r = this.store.getAt(index);
42339         
42340         if(r){
42341             this.onSelect(r, index);
42342         }
42343     },
42344     
42345     onSelect : function(record, index){
42346         
42347         if(this.fireEvent('beforeselect', this, record, index) !== false){
42348         
42349             this.setFromCurrencyData(index > -1 ? record.data : false);
42350             
42351             this.collapse();
42352             
42353             this.fireEvent('select', this, record, index);
42354         }
42355     },
42356     
42357     setFromCurrencyData : function(o)
42358     {
42359         var currency = '';
42360         
42361         this.lastCurrency = o;
42362         
42363         if (this.currencyField) {
42364             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42365         } else {
42366             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42367         }
42368         
42369         this.lastSelectionText = currency;
42370         
42371         //setting default currency
42372         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42373             this.setCurrency(this.defaultCurrency);
42374             return;
42375         }
42376         
42377         this.setCurrency(currency);
42378     },
42379     
42380     setFromData : function(o)
42381     {
42382         var c = {};
42383         
42384         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42385         
42386         this.setFromCurrencyData(c);
42387         
42388         var value = '';
42389         
42390         if (this.name) {
42391             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42392         } else {
42393             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42394         }
42395         
42396         this.setValue(value);
42397         
42398     },
42399     
42400     setCurrency : function(v)
42401     {   
42402         this.currencyValue = v;
42403         
42404         if(this.rendered){
42405             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42406             this.validate();
42407         }
42408     },
42409     
42410     setValue : function(v)
42411     {
42412         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42413         
42414         this.value = v;
42415         
42416         if(this.rendered){
42417             
42418             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42419             
42420             this.inputEl().dom.value = (v == '') ? '' :
42421                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42422             
42423             if(!this.allowZero && v === '0') {
42424                 this.hiddenEl().dom.value = '';
42425                 this.inputEl().dom.value = '';
42426             }
42427             
42428             this.validate();
42429         }
42430     },
42431     
42432     getRawValue : function()
42433     {
42434         var v = this.inputEl().getValue();
42435         
42436         return v;
42437     },
42438     
42439     getValue : function()
42440     {
42441         return this.fixPrecision(this.parseValue(this.getRawValue()));
42442     },
42443     
42444     parseValue : function(value)
42445     {
42446         if(this.thousandsDelimiter) {
42447             value += "";
42448             r = new RegExp(",", "g");
42449             value = value.replace(r, "");
42450         }
42451         
42452         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42453         return isNaN(value) ? '' : value;
42454         
42455     },
42456     
42457     fixPrecision : function(value)
42458     {
42459         if(this.thousandsDelimiter) {
42460             value += "";
42461             r = new RegExp(",", "g");
42462             value = value.replace(r, "");
42463         }
42464         
42465         var nan = isNaN(value);
42466         
42467         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42468             return nan ? '' : value;
42469         }
42470         return parseFloat(value).toFixed(this.decimalPrecision);
42471     },
42472     
42473     decimalPrecisionFcn : function(v)
42474     {
42475         return Math.floor(v);
42476     },
42477     
42478     validateValue : function(value)
42479     {
42480         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42481             return false;
42482         }
42483         
42484         var num = this.parseValue(value);
42485         
42486         if(isNaN(num)){
42487             this.markInvalid(String.format(this.nanText, value));
42488             return false;
42489         }
42490         
42491         if(num < this.minValue){
42492             this.markInvalid(String.format(this.minText, this.minValue));
42493             return false;
42494         }
42495         
42496         if(num > this.maxValue){
42497             this.markInvalid(String.format(this.maxText, this.maxValue));
42498             return false;
42499         }
42500         
42501         return true;
42502     },
42503     
42504     validate : function()
42505     {
42506         if(this.disabled || this.allowBlank){
42507             this.markValid();
42508             return true;
42509         }
42510         
42511         var currency = this.getCurrency();
42512         
42513         if(this.validateValue(this.getRawValue()) && currency.length){
42514             this.markValid();
42515             return true;
42516         }
42517         
42518         this.markInvalid();
42519         return false;
42520     },
42521     
42522     getName: function()
42523     {
42524         return this.name;
42525     },
42526     
42527     beforeBlur : function()
42528     {
42529         if(!this.castInt){
42530             return;
42531         }
42532         
42533         var v = this.parseValue(this.getRawValue());
42534         
42535         if(v || v == 0){
42536             this.setValue(v);
42537         }
42538     },
42539     
42540     onBlur : function()
42541     {
42542         this.beforeBlur();
42543         
42544         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42545             //this.el.removeClass(this.focusClass);
42546         }
42547         
42548         this.hasFocus = false;
42549         
42550         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42551             this.validate();
42552         }
42553         
42554         var v = this.getValue();
42555         
42556         if(String(v) !== String(this.startValue)){
42557             this.fireEvent('change', this, v, this.startValue);
42558         }
42559         
42560         this.fireEvent("blur", this);
42561     },
42562     
42563     inputEl : function()
42564     {
42565         return this.el.select('.roo-money-amount-input', true).first();
42566     },
42567     
42568     currencyEl : function()
42569     {
42570         return this.el.select('.roo-money-currency-input', true).first();
42571     },
42572     
42573     hiddenEl : function()
42574     {
42575         return this.el.select('input.hidden-number-input',true).first();
42576     }
42577     
42578 });/**
42579  * @class Roo.bootstrap.BezierSignature
42580  * @extends Roo.bootstrap.Component
42581  * Bootstrap BezierSignature class
42582  * This script refer to:
42583  *    Title: Signature Pad
42584  *    Author: szimek
42585  *    Availability: https://github.com/szimek/signature_pad
42586  *
42587  * @constructor
42588  * Create a new BezierSignature
42589  * @param {Object} config The config object
42590  */
42591
42592 Roo.bootstrap.BezierSignature = function(config){
42593     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42594     this.addEvents({
42595         "resize" : true
42596     });
42597 };
42598
42599 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42600 {
42601      
42602     curve_data: [],
42603     
42604     is_empty: true,
42605     
42606     mouse_btn_down: true,
42607     
42608     /**
42609      * @cfg {int} canvas height
42610      */
42611     canvas_height: '200px',
42612     
42613     /**
42614      * @cfg {float|function} Radius of a single dot.
42615      */ 
42616     dot_size: false,
42617     
42618     /**
42619      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42620      */
42621     min_width: 0.5,
42622     
42623     /**
42624      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42625      */
42626     max_width: 2.5,
42627     
42628     /**
42629      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42630      */
42631     throttle: 16,
42632     
42633     /**
42634      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42635      */
42636     min_distance: 5,
42637     
42638     /**
42639      * @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.
42640      */
42641     bg_color: 'rgba(0, 0, 0, 0)',
42642     
42643     /**
42644      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42645      */
42646     dot_color: 'black',
42647     
42648     /**
42649      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42650      */ 
42651     velocity_filter_weight: 0.7,
42652     
42653     /**
42654      * @cfg {function} Callback when stroke begin. 
42655      */
42656     onBegin: false,
42657     
42658     /**
42659      * @cfg {function} Callback when stroke end.
42660      */
42661     onEnd: false,
42662     
42663     getAutoCreate : function()
42664     {
42665         var cls = 'roo-signature column';
42666         
42667         if(this.cls){
42668             cls += ' ' + this.cls;
42669         }
42670         
42671         var col_sizes = [
42672             'lg',
42673             'md',
42674             'sm',
42675             'xs'
42676         ];
42677         
42678         for(var i = 0; i < col_sizes.length; i++) {
42679             if(this[col_sizes[i]]) {
42680                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42681             }
42682         }
42683         
42684         var cfg = {
42685             tag: 'div',
42686             cls: cls,
42687             cn: [
42688                 {
42689                     tag: 'div',
42690                     cls: 'roo-signature-body',
42691                     cn: [
42692                         {
42693                             tag: 'canvas',
42694                             cls: 'roo-signature-body-canvas',
42695                             height: this.canvas_height,
42696                             width: this.canvas_width
42697                         }
42698                     ]
42699                 },
42700                 {
42701                     tag: 'input',
42702                     type: 'file',
42703                     style: 'display: none'
42704                 }
42705             ]
42706         };
42707         
42708         return cfg;
42709     },
42710     
42711     initEvents: function() 
42712     {
42713         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42714         
42715         var canvas = this.canvasEl();
42716         
42717         // mouse && touch event swapping...
42718         canvas.dom.style.touchAction = 'none';
42719         canvas.dom.style.msTouchAction = 'none';
42720         
42721         this.mouse_btn_down = false;
42722         canvas.on('mousedown', this._handleMouseDown, this);
42723         canvas.on('mousemove', this._handleMouseMove, this);
42724         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42725         
42726         if (window.PointerEvent) {
42727             canvas.on('pointerdown', this._handleMouseDown, this);
42728             canvas.on('pointermove', this._handleMouseMove, this);
42729             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42730         }
42731         
42732         if ('ontouchstart' in window) {
42733             canvas.on('touchstart', this._handleTouchStart, this);
42734             canvas.on('touchmove', this._handleTouchMove, this);
42735             canvas.on('touchend', this._handleTouchEnd, this);
42736         }
42737         
42738         Roo.EventManager.onWindowResize(this.resize, this, true);
42739         
42740         // file input event
42741         this.fileEl().on('change', this.uploadImage, this);
42742         
42743         this.clear();
42744         
42745         this.resize();
42746     },
42747     
42748     resize: function(){
42749         
42750         var canvas = this.canvasEl().dom;
42751         var ctx = this.canvasElCtx();
42752         var img_data = false;
42753         
42754         if(canvas.width > 0) {
42755             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42756         }
42757         // setting canvas width will clean img data
42758         canvas.width = 0;
42759         
42760         var style = window.getComputedStyle ? 
42761             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42762             
42763         var padding_left = parseInt(style.paddingLeft) || 0;
42764         var padding_right = parseInt(style.paddingRight) || 0;
42765         
42766         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42767         
42768         if(img_data) {
42769             ctx.putImageData(img_data, 0, 0);
42770         }
42771     },
42772     
42773     _handleMouseDown: function(e)
42774     {
42775         if (e.browserEvent.which === 1) {
42776             this.mouse_btn_down = true;
42777             this.strokeBegin(e);
42778         }
42779     },
42780     
42781     _handleMouseMove: function (e)
42782     {
42783         if (this.mouse_btn_down) {
42784             this.strokeMoveUpdate(e);
42785         }
42786     },
42787     
42788     _handleMouseUp: function (e)
42789     {
42790         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42791             this.mouse_btn_down = false;
42792             this.strokeEnd(e);
42793         }
42794     },
42795     
42796     _handleTouchStart: function (e) {
42797         
42798         e.preventDefault();
42799         if (e.browserEvent.targetTouches.length === 1) {
42800             // var touch = e.browserEvent.changedTouches[0];
42801             // this.strokeBegin(touch);
42802             
42803              this.strokeBegin(e); // assume e catching the correct xy...
42804         }
42805     },
42806     
42807     _handleTouchMove: function (e) {
42808         e.preventDefault();
42809         // var touch = event.targetTouches[0];
42810         // _this._strokeMoveUpdate(touch);
42811         this.strokeMoveUpdate(e);
42812     },
42813     
42814     _handleTouchEnd: function (e) {
42815         var wasCanvasTouched = e.target === this.canvasEl().dom;
42816         if (wasCanvasTouched) {
42817             e.preventDefault();
42818             // var touch = event.changedTouches[0];
42819             // _this._strokeEnd(touch);
42820             this.strokeEnd(e);
42821         }
42822     },
42823     
42824     reset: function () {
42825         this._lastPoints = [];
42826         this._lastVelocity = 0;
42827         this._lastWidth = (this.min_width + this.max_width) / 2;
42828         this.canvasElCtx().fillStyle = this.dot_color;
42829     },
42830     
42831     strokeMoveUpdate: function(e)
42832     {
42833         this.strokeUpdate(e);
42834         
42835         if (this.throttle) {
42836             this.throttleStroke(this.strokeUpdate, this.throttle);
42837         }
42838         else {
42839             this.strokeUpdate(e);
42840         }
42841     },
42842     
42843     strokeBegin: function(e)
42844     {
42845         var newPointGroup = {
42846             color: this.dot_color,
42847             points: []
42848         };
42849         
42850         if (typeof this.onBegin === 'function') {
42851             this.onBegin(e);
42852         }
42853         
42854         this.curve_data.push(newPointGroup);
42855         this.reset();
42856         this.strokeUpdate(e);
42857     },
42858     
42859     strokeUpdate: function(e)
42860     {
42861         var rect = this.canvasEl().dom.getBoundingClientRect();
42862         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42863         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42864         var lastPoints = lastPointGroup.points;
42865         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42866         var isLastPointTooClose = lastPoint
42867             ? point.distanceTo(lastPoint) <= this.min_distance
42868             : false;
42869         var color = lastPointGroup.color;
42870         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42871             var curve = this.addPoint(point);
42872             if (!lastPoint) {
42873                 this.drawDot({color: color, point: point});
42874             }
42875             else if (curve) {
42876                 this.drawCurve({color: color, curve: curve});
42877             }
42878             lastPoints.push({
42879                 time: point.time,
42880                 x: point.x,
42881                 y: point.y
42882             });
42883         }
42884     },
42885     
42886     strokeEnd: function(e)
42887     {
42888         this.strokeUpdate(e);
42889         if (typeof this.onEnd === 'function') {
42890             this.onEnd(e);
42891         }
42892     },
42893     
42894     addPoint:  function (point) {
42895         var _lastPoints = this._lastPoints;
42896         _lastPoints.push(point);
42897         if (_lastPoints.length > 2) {
42898             if (_lastPoints.length === 3) {
42899                 _lastPoints.unshift(_lastPoints[0]);
42900             }
42901             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42902             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42903             _lastPoints.shift();
42904             return curve;
42905         }
42906         return null;
42907     },
42908     
42909     calculateCurveWidths: function (startPoint, endPoint) {
42910         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42911             (1 - this.velocity_filter_weight) * this._lastVelocity;
42912
42913         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42914         var widths = {
42915             end: newWidth,
42916             start: this._lastWidth
42917         };
42918         
42919         this._lastVelocity = velocity;
42920         this._lastWidth = newWidth;
42921         return widths;
42922     },
42923     
42924     drawDot: function (_a) {
42925         var color = _a.color, point = _a.point;
42926         var ctx = this.canvasElCtx();
42927         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42928         ctx.beginPath();
42929         this.drawCurveSegment(point.x, point.y, width);
42930         ctx.closePath();
42931         ctx.fillStyle = color;
42932         ctx.fill();
42933     },
42934     
42935     drawCurve: function (_a) {
42936         var color = _a.color, curve = _a.curve;
42937         var ctx = this.canvasElCtx();
42938         var widthDelta = curve.endWidth - curve.startWidth;
42939         var drawSteps = Math.floor(curve.length()) * 2;
42940         ctx.beginPath();
42941         ctx.fillStyle = color;
42942         for (var i = 0; i < drawSteps; i += 1) {
42943         var t = i / drawSteps;
42944         var tt = t * t;
42945         var ttt = tt * t;
42946         var u = 1 - t;
42947         var uu = u * u;
42948         var uuu = uu * u;
42949         var x = uuu * curve.startPoint.x;
42950         x += 3 * uu * t * curve.control1.x;
42951         x += 3 * u * tt * curve.control2.x;
42952         x += ttt * curve.endPoint.x;
42953         var y = uuu * curve.startPoint.y;
42954         y += 3 * uu * t * curve.control1.y;
42955         y += 3 * u * tt * curve.control2.y;
42956         y += ttt * curve.endPoint.y;
42957         var width = curve.startWidth + ttt * widthDelta;
42958         this.drawCurveSegment(x, y, width);
42959         }
42960         ctx.closePath();
42961         ctx.fill();
42962     },
42963     
42964     drawCurveSegment: function (x, y, width) {
42965         var ctx = this.canvasElCtx();
42966         ctx.moveTo(x, y);
42967         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42968         this.is_empty = false;
42969     },
42970     
42971     clear: function()
42972     {
42973         var ctx = this.canvasElCtx();
42974         var canvas = this.canvasEl().dom;
42975         ctx.fillStyle = this.bg_color;
42976         ctx.clearRect(0, 0, canvas.width, canvas.height);
42977         ctx.fillRect(0, 0, canvas.width, canvas.height);
42978         this.curve_data = [];
42979         this.reset();
42980         this.is_empty = true;
42981     },
42982     
42983     fileEl: function()
42984     {
42985         return  this.el.select('input',true).first();
42986     },
42987     
42988     canvasEl: function()
42989     {
42990         return this.el.select('canvas',true).first();
42991     },
42992     
42993     canvasElCtx: function()
42994     {
42995         return this.el.select('canvas',true).first().dom.getContext('2d');
42996     },
42997     
42998     getImage: function(type)
42999     {
43000         if(this.is_empty) {
43001             return false;
43002         }
43003         
43004         // encryption ?
43005         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43006     },
43007     
43008     drawFromImage: function(img_src)
43009     {
43010         var img = new Image();
43011         
43012         img.onload = function(){
43013             this.canvasElCtx().drawImage(img, 0, 0);
43014         }.bind(this);
43015         
43016         img.src = img_src;
43017         
43018         this.is_empty = false;
43019     },
43020     
43021     selectImage: function()
43022     {
43023         this.fileEl().dom.click();
43024     },
43025     
43026     uploadImage: function(e)
43027     {
43028         var reader = new FileReader();
43029         
43030         reader.onload = function(e){
43031             var img = new Image();
43032             img.onload = function(){
43033                 this.reset();
43034                 this.canvasElCtx().drawImage(img, 0, 0);
43035             }.bind(this);
43036             img.src = e.target.result;
43037         }.bind(this);
43038         
43039         reader.readAsDataURL(e.target.files[0]);
43040     },
43041     
43042     // Bezier Point Constructor
43043     Point: (function () {
43044         function Point(x, y, time) {
43045             this.x = x;
43046             this.y = y;
43047             this.time = time || Date.now();
43048         }
43049         Point.prototype.distanceTo = function (start) {
43050             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43051         };
43052         Point.prototype.equals = function (other) {
43053             return this.x === other.x && this.y === other.y && this.time === other.time;
43054         };
43055         Point.prototype.velocityFrom = function (start) {
43056             return this.time !== start.time
43057             ? this.distanceTo(start) / (this.time - start.time)
43058             : 0;
43059         };
43060         return Point;
43061     }()),
43062     
43063     
43064     // Bezier Constructor
43065     Bezier: (function () {
43066         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43067             this.startPoint = startPoint;
43068             this.control2 = control2;
43069             this.control1 = control1;
43070             this.endPoint = endPoint;
43071             this.startWidth = startWidth;
43072             this.endWidth = endWidth;
43073         }
43074         Bezier.fromPoints = function (points, widths, scope) {
43075             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43076             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43077             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43078         };
43079         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43080             var dx1 = s1.x - s2.x;
43081             var dy1 = s1.y - s2.y;
43082             var dx2 = s2.x - s3.x;
43083             var dy2 = s2.y - s3.y;
43084             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43085             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43086             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43087             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43088             var dxm = m1.x - m2.x;
43089             var dym = m1.y - m2.y;
43090             var k = l2 / (l1 + l2);
43091             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43092             var tx = s2.x - cm.x;
43093             var ty = s2.y - cm.y;
43094             return {
43095                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43096                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43097             };
43098         };
43099         Bezier.prototype.length = function () {
43100             var steps = 10;
43101             var length = 0;
43102             var px;
43103             var py;
43104             for (var i = 0; i <= steps; i += 1) {
43105                 var t = i / steps;
43106                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43107                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43108                 if (i > 0) {
43109                     var xdiff = cx - px;
43110                     var ydiff = cy - py;
43111                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43112                 }
43113                 px = cx;
43114                 py = cy;
43115             }
43116             return length;
43117         };
43118         Bezier.prototype.point = function (t, start, c1, c2, end) {
43119             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43120             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43121             + (3.0 * c2 * (1.0 - t) * t * t)
43122             + (end * t * t * t);
43123         };
43124         return Bezier;
43125     }()),
43126     
43127     throttleStroke: function(fn, wait) {
43128       if (wait === void 0) { wait = 250; }
43129       var previous = 0;
43130       var timeout = null;
43131       var result;
43132       var storedContext;
43133       var storedArgs;
43134       var later = function () {
43135           previous = Date.now();
43136           timeout = null;
43137           result = fn.apply(storedContext, storedArgs);
43138           if (!timeout) {
43139               storedContext = null;
43140               storedArgs = [];
43141           }
43142       };
43143       return function wrapper() {
43144           var args = [];
43145           for (var _i = 0; _i < arguments.length; _i++) {
43146               args[_i] = arguments[_i];
43147           }
43148           var now = Date.now();
43149           var remaining = wait - (now - previous);
43150           storedContext = this;
43151           storedArgs = args;
43152           if (remaining <= 0 || remaining > wait) {
43153               if (timeout) {
43154                   clearTimeout(timeout);
43155                   timeout = null;
43156               }
43157               previous = now;
43158               result = fn.apply(storedContext, storedArgs);
43159               if (!timeout) {
43160                   storedContext = null;
43161                   storedArgs = [];
43162               }
43163           }
43164           else if (!timeout) {
43165               timeout = window.setTimeout(later, remaining);
43166           }
43167           return result;
43168       };
43169   }
43170   
43171 });
43172
43173  
43174
43175